www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Coroutines in D

reply Andrej Mitrovic <none none.none> writes:
I'm trying to figure out how to use coroutines in D.

First, I think I've run into some kind of bug. I've followed this C++ example:
http://www.subatomicglue.com/secret/coro/readme.html, and came up with this:

import std.stdio;
import core.thread;

void fiberFunc(size_t arg)
{
    int i = 0;

    foreach (x; 0 .. 1000)
    {
        writefln("fiber waiting (id = %d)", arg);
        Fiber.yield();
    }

    foreach (x; 0 .. 1000)
    {
        writefln("fiber running (iter = %d id = %d)", ++i, arg);
        Fiber.yield();
    }

    while (1)
    {
        writefln("fiber waiting (id = %d)", arg);
        Fiber.yield();
    }
}

void main()
{
    Fiber[200] fibers;

    foreach (ref fiber; fibers)
    {
        fiber = new Fiber(&fiberFunc);
    }

    while (true)
    {
        foreach (fiber; fibers)
        {
            fiber.call();
        }
    }
}

Only after I've translated the C++ example to D I've realized that fiberFunc
actually takes an argument. But from what I can tell core.thread.Fiber never
calls this function with any arguments. I have a hunch that "arg" ends up
having an uninitialized garbage value, so it looks like this is a bug?

Ok, that put aside what I really want to emulate is this Python example of a
coroutine (perhaps bearophile is familiar with this):

def coroutine(func):
    def start(*args,**kwargs):
        cr = func(*args,**kwargs)
        cr.next()
        return cr
    return start

 coroutine
def unwrap_protocol(header='\x61',
                    footer='\x62',
                    dle='\xAB',
                    after_dle_func=lambda x: x,
                    target=None):
    """ Simplified framing (protocol unwrapping)
        co-routine.
    """
    # Outer loop looking for a frame header
    #
    while True:
        byte = (yield)
        frame = ''

        if byte == header:
            # Capture the full frame
            #
            while True:
                byte = (yield)
                if byte == footer:
                    target.send(frame)
                    break
                elif byte == dle:
                    byte = (yield)
                    frame += after_dle_func(byte)
                else:
                    frame += byte

 coroutine
def frame_receiver():
    """ A simple co-routine "sink" for receiving
        full frames.
    """
    while True:
        frame = (yield)
        print 'Got frame:', frame.encode('hex')


bytes = ''.join(chr(b) for b in
            [0x70, 0x24,
             0x61, 0x99, 0xAF, 0xD1, 0x62,
             0x56, 0x62,
             0x61, 0xAB, 0xAB, 0x14, 0x62,
             0x7
            ])

unwrapper = unwrap_protocol(target=frame_receiver())

for byte in bytes:
    unwrapper.send(byte)

This was taken from Eli's blog:
http://eli.thegreenplace.net/2009/08/29/co-routines-as-an-alternative-to-state-machines/

I'm not seeing any way of yielding a value back from a Fiber or even sending
it, so I don't see how I can use fibers to implement coroutines which send or
return values. But I'm new to the concept so maybe I'm missing something
obvious?
May 03 2011
next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
Ok,I've found a post that could be useful:
http://www.digitalmars.com/d/archives/digitalmars/D/yield_C_etc_74821.html

and there's a Generator mixin in generators.d, from the libs_d
library: http://www.fantascienza.net/leonardo/so/index.html

I'll give these a try soon.
May 03 2011
prev sibling parent Robert Clipsham <robert octarineparrot.com> writes:
On 03/05/2011 19:06, Andrej Mitrovic wrote:
 I'm trying to figure out how to use coroutines in D.

 First, I think I've run into some kind of bug. I've followed this C++ example:
http://www.subatomicglue.com/secret/coro/readme.html, and came up with this:

I'm not entirely sure what it is you want to be able to do (I'm rather tired and didn't want to read through the whole example, I'll take another look tomorrow unless someone else beats me to it), but from what I can gather you want to pass values to the fiber between calling and yielding? The way to do this is to derive your fiber rather than composing it: class Derived : Fiber { size_t arg; this() { super(&run); } void func() { writefln("val: %s", arg); Fiber.yield(); writefln("val: %s", arg); } } auto fiber = new Derived; fiber.arg = 6; fiber.call(); fiber.arg = 7; fiber.call(); You have, however, encountered a bug - Fiber should not accept a function with parameters, you should file a bug report for this. Hope this helps. -- Robert http://octarineparrot.com/
May 03 2011