Mega Search
23.2 Million


Sign Up

Make a donation  
stackless coroutines using template parameter for program co  
News Group: comp.lang.c++.moderated

{ Please limit your text to fit within 80 columns, preferably around 70,
  so that readers don't have to scroll horizontally to read each line.
  This article has been reformatted manually by the moderator. -mod }


Hi Everyone,

I've been looking at Clojure's go macro, and have been pondering the
problem of re-entering a C++ class method to implement coroutine like
"yield"/resume. I'm concerned with stackless coroutines (i.e. state
stored in a C++ object, not using stack switching).

The existing C++ implementations that I'm familiar with ([1], [2], [3]),
use Duff's device and __LINE__ to re-enter the method/function at the
appropriate point. The problem is that they all store the resume point
case label in a variable (e.g. in an int member variable) so there is
runtime switch() overhead in doing the resume.

It got me thinking, what you really want is to generate a continuation
function pointer that executes directly from the resume point. I think
that this can be done by encoding the resume point (case label) as an
integer template parameter. Since the resume point is a constant, the
compiler should optimize all of the switch overhead away. Each resume
point then gets its own optimized function.

I'm curious to hear whether anyone else has tried this or seen it
elsewhere. Are there any problems with it compared to the "traditional"
approaches I linked to?

I've implemented two examples:

(a) A coroutine type generator routine:

https://gist.github.com/RossBencina/299766c0783fda0eb993#file-mycoroutine-cpp

Code for this is included below.

and

(b) An async-callback / await type thing:

https://gist.github.com/RossBencina/4925236fa88f1a5e26ac#file-myasyncmethod-cpp

This second example is more interesting because it generates multiple
async callback functions from a single method.

What do you think?

Thanks.


Here's the generator example:

#include 

class MyCoroutine {
    typedef MyCoroutine this_t;
    typedef bool (this_t::*coroutine_fn_t) ();
    coroutine_fn_t next_;

    // This is a stackless co-routine. Any state that needs to be retained
    // across yeild points needs to be stored at class scope.
    // Coroutine state:
    int i;

#define BEGIN_COROUTINE switch (PC) { case 0:;
#define YEILD(result) { next_ = &this_t::coroutine_fn<__LINE__>; return (result); case __LINE__:; }
#define END_COROUTINE {next_ = &this_t::coroutine_fn<__LINE__>; case __LINE__:;} }

    template
    bool coroutine_fn() {
        BEGIN_COROUTINE

        std::cout << "before loop" << std::endl;
        YEILD(true);

        for(i=0; i < 5; ++i ){
            std::cout << "in loop " << i << std::endl;
            YEILD(true);
        }

        std::cout << "after loop A" << std::endl;
        YEILD(true);
        std::cout << "after loop B" << std::endl;

        END_COROUTINE
        return false; // Will repeat this value after termination.
    }

public:
    MyCoroutine()
        : next_( &this_t::coroutine_fn<0> )
    {
    }

    bool step() // returns true while continuing
    {
        return (this->*next_)();
    }
};


int main(int argc, char* argv[])
{
    std::cout << "*** Coroutine ***" << std::endl;
    MyCoroutine coro;

    while (coro.step()) /* nothing */ ;

    std::cout << std::endl;

    return 0;
}

//

References:

[1] http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html

[2] http://dunkels.com/adam/pt/index.html

[3] http://blog.think-async.com/2010/03/potted-guide-to-stackless-coroutines.html


-- 
      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

Vote for best question.
Score: 0  # Vote:  0
Date Posted: 11-May-2014, at 10:37 PM EST
From: Ross Bencina