J'entends beaucoup parler des coroutines ces derniers temps. On me le présente comme quelque chose de formidable, "des threads coopératives, sans les problèmes de concurrence". Lua semble avoir porté cette mode.

Les coroutines ne sont finalement que des threads non-préemptives. Tous le monde se souvient du formidable scheduler de Windows 3.1.

Je suis tombé sur des articles horribles (disons plutôt incomplets) sur les coroutines en C qui me font pensé qu'on petit rappel des syscall Posix s'impose.

Un petit tour sur la documentation de l'appel système makecontext nous permet de faire des choses assez lisible:

      #include <ucontext.h>
      #include <stdio.h>
      #include <stdlib.h>
      
      static ucontext_t uctx_main, uctx_func1, uctx_func2;
      
      #define handle_error(msg) \
          do { perror(msg); exit(EXIT_FAILURE); } while (0)
       
      static void func1(void)
      {
          printf("func1: started\n");
          printf("func1: swapcontext(&uctx_func1, &uctx_func2)\n");
          if (swapcontext(&uctx_func1, &uctx_func2) == -1)
              handle_error("swapcontext");
          printf("func1: returning\n");
      }
       
      static void func2(void)
      {
          printf("func2: started\n");
          printf("func2: swapcontext(&uctx_func2, &uctx_func1)\n");
          if (swapcontext(&uctx_func2, &uctx_func1) == -1)
              handle_error("swapcontext");
          printf("func2: returning\n");
      }
       
      int main(int argc, char *argv)
      {
          char func1_stack[16384];
          char func2_stack[16384];
          
          if (getcontext(&uctx_func1) == -1)
              handle_error("getcontext");
          uctx_func1.uc_stack.ss_sp = func1_stack;
          uctx_func1.uc_stack.ss_size = sizeof(func1_stack);
          uctx_func1.uc_link = &uctx_main;
          makecontext(&uctx_func1, func1, 0);
         
          if (getcontext(&uctx_func2) == -1)
              handle_error("getcontext");
          uctx_func2.uc_stack.ss_sp = func2_stack;
          uctx_func2.uc_stack.ss_size = sizeof(func2_stack);
          /* Successor context is f1(), unless argc > 1 */
          uctx_func2.uc_link = (argc > 1) ? NULL : &uctx_func1;
          makecontext(&uctx_func2, func2, 0);
         
          printf("main: swapcontext(&uctx_main, &uctx_func2)\n");
          if (swapcontext(&uctx_main, &uctx_func2) == -1)
              handle_error("swapcontext");
          
          printf("main: exiting\n");
          exit(EXIT_SUCCESS);
      }