Les coroutines

Attention, les bases du langages doivent être bien maitrisées avant d’attaquer cette partie. N’hésitez pas à la relire plusieurs fois

N’utilisez les coroutines que si vous savez ce que vous faites !

Les coroutines sont comme les threads en C mais avec une différence de taille. Un programme Lua peut avoir plusieurs coroutines de créées mais seule une peut fonctionner à la fois ET elle arrête le programme principal tant qu’elle n’a pas rendu la main !

Utilisation

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
-- Création d'une fonction qui affichera Bonjour sur la console
function f()
  print("Bonjour")
end

-- Création de la coroutine. Attention, à ce stade, elle est simplement créée !
co = coroutine.create(f)
-- Affichage de l'adresse mémoire de la coroutine
print(co)
-- Exécution de la coroutine
coroutine.resume(co)

-- output:
--  thread: 0x64
--  Bonjour

Même code mais en utilisant une fonction anonyme :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
local co = coroutine.create(function()
    print("Bonjour")
end)

print(co)
coroutine.resume(co)

-- output:
--  thread: 0xa8
--  Bonjour

Visualisation que seule la coroutine est en fonctionnement. On dit qu’elle est bloquante :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
function f()
  print("Début de la coroutine")
  local t0 = os.clock()
  -- On attend 5s
  while os.clock() - t0 <= 5 do
  end
  print("Fin de la coroutine")
end

co = coroutine.create(f)
coroutine.resume(co)
print("Fin du programme")

-- output:
--  Début de la coroutine
--  Fin de la coroutine (après 5s)
--  Fin du programme

Il est possible de connaître l’état d’une coroutine avec coroutine.status(). Elle peut avoir 3 états :

État de la coroutine Signification
suspended En attente
running En fonctionnement
dead Morte !

Une coroutine qui a l’état dead (morte) ne peut être réutilisée !

La vraie puissance des coroutines provient de la fonction coroutine.yield. Celui-ci autorise une reprise ultérieure de son fonctionnement :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
-- Fonction qui sera utilisée pour la coroutine
function f()
  for i=0, 2 do
    print(i)
    -- coroutine.yield() va mettre en "pause" celle-ci et redonner la main au programme principal
    coroutine.yield()
  end
end

co = coroutine.create(f)
-- Appel à la coroutine une 1ère fois
coroutine.resume(co)
-- Appel à la coroutine une 2ème fois
coroutine.resume(co)
-- Appel à la coroutine une 3ème fois
coroutine.resume(co)
-- Attention à la subtilité. La coroutine n'a pas fini la dernière boucle même si tous les chiffres ont été affichés. Regardons:
print(coroutine.status(co))
-- Terminons là !
coroutine.resume(co)
-- Affichage de l'état de la coroutine
print(coroutine.status(co))
-- On essaie de relancer la coroutine (nous allons avoir une erreur)
coroutine.resume(co)

-- output:
--  0
--  1
--  2
--  suspended
--  dead
--  cannot resume dead coroutine

Les générateurs

Passage et retour de paramètres :

1
2
3
4
5
6
7
8
9
function somme(a, b)
  coroutine.yield(a + b)
end

co = coroutine.create(somme)
print(coroutine.resume(co, 6, 4))

-- output:
--  10

Les itérateurs

Les coroutines étant un mécanisme très puissant, elles permettent de construire, en plus des générateurs, des itérateurs. Nous pourrions utiliser les closures mais il est parfois plus simple de le faire avec les coroutines :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
-- Déclaration de la coroutine
function f()
  local tab = {1,2,3,4,5}
  for i=1, #tab do
    -- Retour de la valeur et mise en pause de la coroutine
    coroutine.yield(tab[i])
  end
end

-- Création de la coroutine
co = coroutine.create(f)
-- On affiche toutes les valeurs de la coroutine dans une boucle infinie
while true do
  -- On récupère l'état de la coroutine et le paramètre
  local code, res = coroutine.resume(co)
  -- Si la coroutine est terminée, on casse la boucle
  if not code then break end
  -- Sinon on affiche la valeur renvoyée par la coroutine
  print(res)
end

-- output:
--  1
--  2
--  3
--  4
--  5

Les exemples sont extrêmement basiques. Les coroutines permettent encore bien des choses qui seront abordés dans les tutos pour utilisateurs intermédiaires/experts