Lua - Chaining Coroutines



Chaining coroutines is a nice way to organize operations or create complex control flows. We can make one coroutine yield its execution to another coroutine and then resume the earlier coroutine where it left off.

Most common way to chain coroutines is to use coroutine.yield() and coroutine.resume() methods.

Example - Chaining Using yield() and resume() methods

Using yield() and resume() methods is the most common way to chaining coroutines. A coroutine explicitly yields a value to indicate the next coroutine to be resumed. Now main thread is to pick up the signal and then resume the appropriate coroutine.

Define coroutines

-- function to represent coroutine1 
function coroutine1()
   print("Coroutine #1 started")
   -- yield the current coroutine and signal to switch to next coroutine
   coroutine.yield("switch", co2) 
   print("Coroutine #1 resumed")
end

-- function to represent coroutine2
function coroutine2()
   print("Coroutine #2 started")
   -- yield the current coroutine and signal to switch to next coroutine
   coroutine.yield("switch", co3) 
   print("Coroutine #2 resumed")
end

-- function to represent coroutine3
function coroutine3()
   print("Coroutine #3 started")
   -- yield the current coroutine and signal as Done to mark end of chain.
   coroutine.yield("done")
   print("Coroutine #3 resumed")
end

Create coroutines

-- create co1 as first coroutine
co1 = coroutine.create(coroutine1)
-- create co2 as second coroutine
co2 = coroutine.create(coroutine2)
-- create co3 as third coroutine
co3 = coroutine.create(coroutine3)

-- set a current coroutine as co1
currentCo = co1

In main loop, chain coroutines as per yield values

-- infinite loop
while true do
   -- start the current coroutine
   local status, result = coroutine.resume(current_co)
  
   -- if status is false, print the error
   if not status then
      print("Error:", result)
      break
   end

   -- if value is switch
   if result == "switch" then
      -- get the second argument as next coroutine
      currentCo = select(2, ...) 
   elseif result == "done" then  -- end the chain
      print("All coroutines finished")
      break
   end
end

Complete Example - Chaining coroutines

main.lua

-- function to represent coroutine1 
function coroutine1()
   print("Coroutine #1 started")
   -- yield the current coroutine and signal to switch to next coroutine
   coroutine.yield("switch", co2) 
end

-- function to represent coroutine2
function coroutine2()
   print("Coroutine #2 started")
   -- yield the current coroutine and signal to switch to next coroutine
   coroutine.yield("switch", co3) 
end

-- function to represent coroutine3
function coroutine3()
   print("Coroutine #3 started")
   -- yield the current coroutine and signal as Done to mark end of chain.
   coroutine.yield("done")
end

-- create co1 as first coroutine
co1 = coroutine.create(coroutine1)
-- create co2 as second coroutine
co2 = coroutine.create(coroutine2)
-- create co3 as third coroutine
co3 = coroutine.create(coroutine3)

-- set a current coroutine as co1
currentCo = co1

-- infinite loop
while true do
   -- start the current coroutine
   local status, result, co = coroutine.resume(currentCo)
  
   -- if status is false, print the error
   if not status then
      print("Error:", result)
      break
   end

   -- if value is switch
   if result == "switch" then
      -- get the second argument as next coroutine
      currentCo = co
   elseif result == "done" then  -- end the chain
      print("All coroutines finished")
      break
   end
end

Output

When we run the above code, we will get the following output−

Coroutine #1 started
Coroutine #2 started
Coroutine #3 started
All coroutines finished

Explanation

  • Coroutine1 yiels a value "switch" and second value as coroutine instance co2 to be executed next.

  • In main loop, we're checking the value as "switch" and then setting the currentCo as the yielded coroutine co2.

  • Coroutine2 does the same and yields

  • Coroutine1 yiels a value "switch" and second value as coroutine instance co3.

  • Coroutine3 yiels a value "done" to mark the end of coroutine chain.

Advertisements