Nous allons implanter un mécanisme de coroutines. Les coroutines sont des procédures qui s’exécutent dans des contextes séparés. Ainsi une procédure ping peut « rendre la main » à une procédure pong sans terminer, et la procédure pong peut faire de même avec la procédure ping ensuite, ping reprendra son exécution dans le contexte dans lequel elle était avant de passer la main. Notre ping-pong peut aussi se jouer à plus de deux...
struct ctx_s ctx_ping; struct ctx_s ctx_pong; void f_ping(void *arg); void f_pong(void *arg); int main(int argc, char *argv[]) { init_ctx(&ctx_ping, 16384, f_ping, NULL); init_ctx(&ctx_pong, 16384, f_pong, NULL); switch_to_ctx(&ctx_ping); exit(EXIT_SUCCESS); } void f_ping(void *args) { while(1) { printf("A") ; switch_to_ctx(&ctx_pong); printf("B") ; switch_to_ctx(&ctx_pong); printf("C") ; switch_to_ctx(&ctx_pong); } } void f_pong(void *args) { while(1) { printf("1") ; switch_to_ctx(&ctx_ping); printf("2") ; switch_to_ctx(&ctx_ping); } }
L’exécution de ce programme produit sans fin :
A1B2C1A2B1C2A1...
Cet exemple illustre la procédure
void switch_to_ctx(struct ctx_s *ctx) ;
qui sauvegarde simplement les pointeurs de pile dans le contexte courant, puis définit le contexte dont l’adresse est passée en paramètre comme nouveau contexte courant, et en restaure les registres de pile. Ainsi lorsque cette procédure exécute return; elle « revient » dans le contexte d’exécution passé en paramètre.
Si le contexte est activé pour la première fois, au lieu de revenir avec un return; la fonction appelle f(args) pour « lancer » la première exécution... Attention, après que les registres de piles aient été initialisés sur une nouvelle pile d’exécution pour la première fois, les variables locales et les arguments de la fonction switch_to_ctx() sont inutilisables (ils n’ont pas été enregistrés sur la pile d’exécution).