Quick Links: Download Gideros Studio | Gideros Documentation | Gideros Development Center | Gideros community chat | DONATE
Coroutine.resume() inside Timer completion function — Gideros Forum

Coroutine.resume() inside Timer completion function

bowerandybowerandy Guru
edited February 2013 in Bugs and issues
Is there a reason why it doesn't seem to be possible to resume a coroutine from within a Timer completion function? I have started to use coroutines much more often now for chaining animation sequences. They're significantly more convenient and flexible to use than just adding onComplete handlers to GTweens.

Try this:
local function printHelloAndYieldFor2()
	print("Hello")
	Timer.delayedCall(2000, function() coroutine.resume(coFunc) end)
	coroutine.yield()
end
 
coFunc=coroutine.create(
	function()
		for i=1, 6 do
			printHelloAndYieldFor2()
		end
	end)
 
coroutine.resume(coFunc)
I would expect to see "Hello" printed out 6 times, once every 2 seconds.

best regards

Comments

  • atilimatilim Maintainer
    edited February 2013
    Hi Andy,

    If you change the line
    	Timer.delayedCall(2000, function() coroutine.resume(coFunc) end)
    with
    	Timer.delayedCall(2000, function() print(coroutine.resume(coFunc)) end)
    you can see the error which is "cannot resume running coroutine".

    As you know resume runs in protected mode. Therefore, if there is any error inside a coroutine, Lua will not show the error message, but instead will return it to the resume call.

    The solution is resuming the your coroutine inside another coroutine :)

    Once I wrote a simple wait function with Timer.delayedCall and coroutines, hope it helps:
    local function wait(delay)
        local co = coroutine.running()
        local co2 = coroutine.create(function() Timer.delayedCall(delay, function() coroutine.resume(co) end) end)
        coroutine.resume(co2)    
        coroutine.yield()
    end
     
    local function foo()
        print("foo 1")    
        wait(1000)
        print("foo 2")
        wait(1000)
        print("foo 3")
    end
     
    local co = coroutine.create(foo)
    coroutine.resume(co)
  • atilimatilim Maintainer
    Accepted Answer
    Also that wait function can be shortened a little bit with coroutine.wrap function:
    local function wait(delay)
        local co = coroutine.running()
        coroutine.wrap(function() Timer.delayedCall(delay, function() coroutine.resume(co) end) end)()
        coroutine.yield()
    end
  • bowerandybowerandy Guru
    edited February 2013
    @atilim, wow, thanks for that. The wrap() method seems like it should solve it but I doubt I could have worked that out for myself!

    I guess that ENTER_FRAME events are handled on another (coroutine) thread then, because resuming inside an ENTER_FRAME handler does seems to work okay.

    best regards
  • atilimatilim Maintainer
    edited February 2013
    I remember I had spent a couple of hours to write a correct wait() function. :)

    I guess that ENTER_FRAME events are handled on another (coroutine) thread then, because resuming inside an ENTER_FRAME handler does seems to work okay.
    Interesting. ENTER_FRAME and timer events are handled same. I cannot guess why ENTER_FRAME handler seems to work okay. (Maybe ENTER_FRAME doesn't work also :) ) (as a side note, I don't create or run any coroutines inside Gideros runtime. All function calls are handled by call() or pcall()).
  • bowerandybowerandy Guru
    edited February 2013
    @atilim, interesting.

    This is what I've been doing to work around the problem. Do you remember that postToUpdateQueue() function I mentioned some time ago. Well, I implemented it using a dummy GTween with a completion function. IIRC, GTween uses ENTER_FRAME events to dispatch its completion functions.
    function EventDispatcher:postToUpdateQueue(func)
    	GTween.new(self, 0.001, {}, {onComplete=func})
    end
     
    local function safeResume(coFunc)
    	stage:postToUpdateQueue(function() coroutine.resume(coFunc) end)
    end
    Using safeResume() appears to work for me but only when the coroutine() is originallyy kicked off inside some form of callback like a touch handler. I think your coroutine.wrap() solution is neater so I'll try that instead.

    best regards
  • @atilim, I'm getting a crash while using your wait() solution (and it also occurs with my postToUpdateQueue solution too). The crash appears to occur some time after having used any Timer within a coroutine. It seems to be in the C++ destructor for Timers so I'm guessing it might be a GC issue.

    I'm having difficulty isolating a simple test case but if it helps, here is my wait code and a stack trace in Xcode after the crash.
    function coroutine.wait(delay)
    	local coFunc = coroutine.running()
    	local co=coroutine.create(function() Timer.delayedCall(delay*1000, function() coroutine.resume(coFunc) end) end)
    	coroutine.resume(co)
    --	postToUpdateQueue(function() Timer.delayedCall(delay*1000, function() coroutine.resume(coFunc) end) end)
    	coroutine.yield()
    end
    best regards
    Screen Shot 2013-02-21 at 11.17.11.png
    1106 x 972 - 333K
  • atilimatilim Maintainer
    @bowerandy thanks for the bug report. I'll look into it.
Sign In or Register to comment.