Bibliothèque coroutine

La bibliothèque coroutine permet de créer des coroutines : des fonctions dont l'exécution peut être suspendue (yield) puis reprise (resume) à volonté, un peu comme des threads coopératifs qui ne s'exécutent jamais en parallèle. Elle est chargée automatiquement par OpenLibs() — aucun require n'est nécessaire pour l'utiliser.

local co = coroutine.create(function(a)
    print("premier resume, a =", a)
    local b = coroutine.yield(a + 1)
    print("second resume, b =", b)
    return "terminé"
end)

print(coroutine.resume(co, 10))   --> true  premier resume, a = 10 / true 11
print(coroutine.resume(co, 20))   --> second resume, b = 20 / true terminé
print(coroutine.status(co))       --> dead

Vue d'ensemble

Fonction Description
coroutine.create(f) Crée une coroutine à partir de la fonction f, non démarrée
coroutine.resume(co, ...) Démarre ou reprend l'exécution de co
coroutine.yield(...) Suspend la coroutine appelante, en repassant les valeurs à resume
coroutine.status(co) Statut de co : "running"/"suspended"/"normal"/"dead"
coroutine.running() Coroutine en cours d'exécution, plus un booléen « est le thread principal »
coroutine.isyieldable([co]) true si co (par défaut, la coroutine en cours) peut être suspendue
coroutine.wrap(f) Comme create, mais retourne directement une fonction qui reprend la coroutine
coroutine.close(co) Ferme une coroutine suspendue ou terminée, exécutant ses TBC en attente

coroutine.create(f)

Crée une nouvelle coroutine dont le corps est la fonction f. Retourne un objet de type "thread", initialement à l'état "suspended" (elle n'a pas encore commencé à s'exécuter — le premier resume la démarre).

local co = coroutine.create(function() print("bonjour") end)
print(type(co))   --> thread

coroutine.resume(co [, val1, ···])

Démarre ou continue l'exécution de la coroutine co :

Si la coroutine s'exécute sans erreur, resume retourne true suivi des valeurs passées à yield (si elle se suspend) ou des valeurs retournées par le corps (si elle se termine). En cas d'erreur dans la coroutine, resume retourne false suivi du message d'erreur — l'erreur n'est pas levée dans le contexte appelant, elle doit être testée explicitement :

local co = coroutine.create(function() error("oups") end)
local ok, err = coroutine.resume(co)
print(ok, err)   --> false   ...: oups

Reprendre une coroutine déjà "dead" ou "running"/"normal" retourne également false plus un message d'erreur, plutôt que de lever une erreur script.

coroutine.yield(···)

Suspend l'exécution de la coroutine appelante. Les arguments deviennent les valeurs de retour supplémentaires du resume correspondant. Les valeurs passées au resume suivant deviennent le résultat de cet appel à yield :

local co = coroutine.create(function()
    local x = coroutine.yield()
    print("reçu :", x)
end)
coroutine.resume(co)
coroutine.resume(co, "salut")   --> reçu : salut

Appeler coroutine.yield en dehors d'une coroutine (depuis le thread principal), ou depuis un contexte non-suspendable, est une erreur.

coroutine.wrap(f)

Comme coroutine.create, crée une nouvelle coroutine avec corps f, mais retourne directement une fonction qui la reprend à chaque appel (au lieu de retourner la coroutine elle-même). Contrairement à coroutine.resume, une erreur dans la coroutine n'est pas ramenée sous forme de valeur : elle est propagée dans le contexte appelant, comme une erreur normale (à intercepter avec pcall si besoin).

local gen = coroutine.wrap(function()
    for i = 1, 3 do coroutine.yield(i) end
end)
print(gen(), gen(), gen())   --> 1  2  3

coroutine.status(co)

Retourne le statut de co sous forme de chaîne :

Statut Signification
"running" co est celle qui appelle status (elle s'exécute actuellement)
"suspended" co est suspendue sur un yield, ou n'a pas encore démarré
"normal" co est active mais pas en cours d'exécution (elle a repris une autre coroutine)
"dead" co a terminé son corps, normalement ou par une erreur

coroutine.running()

Retourne deux valeurs : la coroutine en cours d'exécution, et un booléen valant true si cette coroutine est le thread principal (celui qui existe en dehors de tout coroutine.create).

coroutine.isyieldable([co])

Retourne true si co (par défaut, la coroutine en cours) peut actuellement être suspendue par yield. Une coroutine n'est pas suspendable si c'est le thread principal.

coroutine.close(co)

Ferme la coroutine co : exécute ses variables to-be-closed (TBC) en attente et la place à l'état "dead". co doit être "dead" ou "suspended" — la fermer alors qu'elle est "running" ou "normal" est une erreur script.

Retourne true en cas de succès, ou false plus le message d'erreur si la fermeture d'une valeur TBC (ou l'erreur d'origine qui avait arrêté la coroutine) a échoué :

local co = coroutine.create(function()
    local guard <close> = setmetatable({}, { __close = function() print("fermé") end })
    coroutine.yield()
end)
coroutine.resume(co)
print(coroutine.close(co))   --> fermé  / true