/*
 * Este exemplo mostra a utilizacao das funcoes makecontext e switchcontext no linux.
 * Compilar usando: gcc exemplo-ctxt.c -o exemplo-ctxt
 */

#include <stdio.h>
#include <ucontext.h>

static ucontext_t ctx[2], mainctx;
#define ID_F 0
#define ID_G 1

#define STACK_SIZE 4096 /* Deve ser multiplo de 4*/
char     stack[2][STACK_SIZE];

/* As funções neste caso devem ser exatamente do tipo abaixo para o stack ser montado */
void f(int);
void g(int);

void setup(int i)
{
    /* Primeiro, salva o contexto atual (main, executando certa iteracao do loop) */
    getcontext(&ctx[i]);

    /* Cada thread deve executar em uma pilha propria, identicada pelo SP */
    ctx[i].uc_stack.ss_sp   = stack[i];
    ctx[i].uc_stack.ss_size = STACK_SIZE;
    ctx[i].uc_link          = &mainctx;

    /* Como esse contexto nao é exatamente o que se deseja, muda o PC para função */
    /* Define:  contexto,      a função a executar,  no. de parametros e valor do param. */
    makecontext( &ctx[i], (void(*)(void))(i? g: f),                  1,     (i+1)*1000);
}

int main()
{
    getcontext(&mainctx);
    setup(ID_F);
    setup(ID_G);

    /* Agora, transfere a execução para f como se ela acabasse de ser chamada */
    swapcontext(&mainctx,&(ctx[0]));

    fprintf(stderr,"Erro: nunca deveria retornar a este ponto!!!\n");

    return 0;
}

#define SECOND 1000000

void f(int param)
{
  int i=0;

  printf("Em f pela primeira vez: param=%d\n",param);

  /* Identifica a execução, espera um segundo, muda de contexto salvando o corrente */
  while(1){
    printf("  f (%d)\n",i++);
    usleep(SECOND);
    swapcontext(&ctx[0], &ctx[1]);
  }
}

void g(int param)
{
  int i=0;

  printf("Em g pela primeira vez: param=%d\n",param);

  /* Identifica a execução, espera um segundo, muda de contexto salvando o corrente */
  while(1){
    printf("  g (%d)\n",i++);
    usleep(SECOND);
    swapcontext(&ctx[1], &ctx[0]);
  }
}
