Lua - Nested Coroutines



Nesting coroutines refers to creating coroutine(s) during a coroutine execution, resuming other coroutine and so. Nesting coroutines allows to create complex workflows. In this chapter, we're discussing the ways to handle coroutine nesting and implications.

Example - Creation and Resumption within Coroutine

Let's look at an example to understand the concept of nesting of coroutines.

main.lua

-- child coroutine
function coroutine_child()
  print("Child Started.")
  coroutine.yield("Child Yielded.")
  print("Child Finished.")
  return "Child Result."
end

function coroutine_parent()
  print("Parent Started.")
  local co_child = coroutine.create(coroutine_child)
  local status, value = coroutine.resume(co_child)
  print("Parent resumed child, status:", status, "yield:", value)

  -- resume the child to finish its execution
  status, result = coroutine.resume(co_child)
  print("Parent resumed child again, status:", status, "result:", result)
  print("Parent Finished.")
end

-- create a parent coroutine
co_parent = coroutine.create(coroutine_parent)
-- start the parent coroutine
coroutine.resume(co_parent)

Output

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

Parent Started.
Child Started.
Parent resumed child, status:	true	yield:	Child Yielded.
Child Finished.
Parent resumed child again, status:	true	result:	Child Result.
Parent Finished.

Explanation

  • coroutine_parent coroutine is creating a coroutine_child coroutine.

  • Next, parent coroutine is explicitly starting the child coroutine and a messge is printed.

  • Finally, parent again resume the child coroutine to finish its execution.

Example - Multilevel Nesting of Coroutine

We can nest coroutines to any level where parent creates a child and child creates a grandchild and so on. But such scenario needs careful consideration on communication between coroutines.

main.lua

-- child coroutine
function coroutine_grandchild()
  print("Grandchild Started.")
  coroutine.yield("Grandchild Yielded.")
  print("Grandchild Finished.")
  return "Grandchild Result."
end

-- child coroutine
function coroutine_child()
  print("Child Started.")
  local co_grandchild = coroutine.create(coroutine_grandchild)
  local status, value = coroutine.resume(co_grandchild)
  print("Child resumed grandchild, status:", status, "yield:", value)

  -- resume the grandchild to finish its execution
  coroutine.resume(co_grandchild)
  coroutine.yield("Child Yielded.")
  print("Child Finished.")
end

function coroutine_parent()
  print("Parent Started.")
  local co_child = coroutine.create(coroutine_child)
  local status, value = coroutine.resume(co_child)
  print("Parent resumed child, status:", status, "yield:", value)

  -- resume the child to finish its execution
  coroutine.resume(co_child)
  print("Parent Finished.")
end

-- create a parent coroutine
co_parent = coroutine.create(coroutine_parent)
-- start the parent coroutine
coroutine.resume(co_parent)

Output

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

Parent Started.
Child Started.
Grandchild Started.
Child resumed grandchild, status:	true	yield:	Grandchild Yielded.
Grandchild Finished.
Parent resumed child, status:	true	yield:	Child Yielded.
Child Finished.
Parent Finished.

Explanation

  • parent coroutine is creating a child coroutine.

  • child coroutine is creating a grandchild coroutine.

  • We need to take care of chain of resume to propagate result.

Applications of Nesting Coroutines

  • Subtasks with larger Task − A parent coroutine can initiate multiple subtasks as child subtasks and meanwhile it can perform any asynchronous operation which might be a time taking process.

  • Modularize Complex Algorithm − We can break a big complex algorithm to small breakable parts as sub-coroutines while parent coroutine has to manage their executions as per algorithm's requirement.

  • Actors Implementation − We can implement actors as nested coroutines where an actor is to maintain its own state and interacts with other actors as coroutines.

Advertisements