Sleep in a coroutine


#1

Hello everyone,
after trying for some time by myself i guess it’s time to ask for help.

Scenario:
start an animation chain
wait for it to end
start another animation chain

I want to avoid the use of a last callback on chain 1 to fire the second set of animations as everything wil become messy as i would have to pass down the callback thru many subroutines

So i’m exploring coroutines and wrote:

function waitAnimationEnd()
	while(animating) do
                 socket.sleep(0.1)
	end
end

function checkMatchingGlyphs()
	print("check matching")
	co1 = coroutine.create(function()
		checkMatchingGlyph(C.PLAYER1)
	end)
	co2 = coroutine.create(function()
		waitAnimationEnd()
		checkMatchingGlyph(C.PLAYER2)
	end)
	coroutine.resume(co1)
	coroutine.resume(co2)
end

the animating flag is set and reset properly

however, if i resume coroutine(co2), everything freezes while i expected that sleeping in the coroutine would not affect the main thread
what i did not understood?


#2

You are using socket.sleep which

Freezes the program execution during a given amount of time.

You probably want to use coroutine.yield which
Suspends the execution of the calling coroutine.


Sidenote:
I would recommend using something like @britzl’s flow to simplify working with coroutines


#3

You’re using coroutines wrong. As Mattias points out socket.sleep() is not the way to go since it will block the thread. Here’s an example of running two animations using go.animate() one after the other using a coroutine:

local function go_animate(url, property, playback, to, easing, duration, delay)
	-- get the current coroutine
	local co = coroutine.running()
	assert(co, "You must call this function from within a coroutine")
	-- start animation
	go.animate(url, property, playback, to, easing, duration, delay, function()
		-- animation is done, continue the coroutine
		coroutine.resume(co)
	end)
	-- yield/pause the coroutine
	coroutine.yield()
end

local co = coroutine.create(function()
	go_animate("go1", "position.x", go.PLAYBACK_ONCE_FORWARD, 100, go.EASING_INOUTQUAD, 2, 0)
	go_animate("go1", "position.y", go.PLAYBACK_ONCE_FORWARD, 500, go.EASING_INOUTQUAD, 4, 0)
end)
coroutine.resume(co)

#4

indeed…i was assuming a coroutine is executing in a different thread, so sleeping in that thread won’t affect the main thread
anyway, between the flow module and your example i should be able to get around this
thanks!


#5

The flow module is great!

FLOW.start(function()
		checkMatchingGlyph(C.PLAYER1)
		FLOW.until_true(isCheckingGlyphCompleted)
		checkMatchingGlyph(C.PLAYER2)
	end)

#6

Yes it can be useful. One question though: what causes a state transition that makes is checkinG glyphComplete return true?


#7

coroutines are similar to a thread but it is not.
https://www.lua.org/pil/9.html


#8

Something like this:

function isCheckingGLyphCompleted()
	return not checkingGlyphOngoing
end

local function freezeGlyphCompleted(player)
	-- recheck for other matches, can happen after a board swap
	-- pass back true so we will generate a new glyph
	checkMatchingGlyph(player, true)
end

-- check for a match for the given player
-- anyFound is set after finding a match in the freeze completed callback
function checkMatchingGlyph(player, anyFound)
	print("check matching for player " .. player)
	inputEnabled = false
	checkingGlyphOngoing = true
	local glyph = M.getPlayers().getGlyph(player)
	local match = M.getBoard().checkMatchingGlyph(player, glyph)
	if(match ~= nil) then
		-- freeze the match on the board
		M.getBoard().freezeGlyph(match, glyph, freezeGlyphCompleted(player))
	else
                -- no more matches found, if we found at least one, generate anew glyph
		if(anyFound) then
			M.newGlyph(player)
		end
		inputEnabled = true
		checkingGlyphOngoing = false
	end
end