Search code examples
asynchronousgtkyieldglibvala

Vala yield not returning


I'm having trouble writing an async function in Vala.
I understand that the function's callback has to get registered somewhere in order for execution to continue after the yield statements, so I add it to the GLib mainloop to be called periodically using Idle.add. Here is my dummy code:

async void loop_func()
{
    var id = Idle.add(loop_func.callback);
    message("Added idle %u",id);

    yield;
    message("yielded successfully 1");

    yield;
    message("yielded successfully 2");

    for (var i = 0; i < 20; i++)
    {
        message("%d",i);
        yield;
    }

    message("finished");
}

int main()
{
    var loop = new GLib.MainLoop();
    loop_func.begin(() => {loop.quit();});
    loop.run();
    return 0;
}

Despite this, the code after the 2nd yield statement never gets executed. This can be seen from the output:

$ ./async   
** Message: 20:07:24.932: async.vala:4: Added idle 1
** Message: 20:07:24.932: async.vala:7: yielded successfully 1

And then it hangs.
What am I missing here?


Solution

  • Although GLib calls an idle function repeatedly until it tells it to stop (by returning false), the async function's .callback seems to quit repeating immediately after being called.

    This can be overcome by adding the callback again every time before the yield statement:

    async void loop_func()
    {
        for (var i = 0; i < 20; i++)
        {
            Idle.add(loop_func.callback);
            yield;
        }
    }
    

    An alternative (and possibly more effective) way to do this would be to ignore the value that the async function's callback returns:

    async void loop_func()
    {
        var id = Idle.add(() => {loop_func.callback(); return Source.CONTINUE;});
    
        for (var i = 0; i < 20; i++)
        {
            yield;
        }
    
        Source.remove(id);
    }