
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <ucontext.h>
#include <sys/time.h>

/* @r{Set by the signal handler.} */
static volatile int expired;

/* @r{The contexts.} */
static ucontext_t uc[3];

/* @r{We do only a certain number of switches.} */
static int switches;


/* @r{This is the function doing the work.  It is just a
   skeleton, real code has to be filled in.} */
static void
f (int n)
@{
  int m = 0;
  while (1)
    @{
      /* @r{This is where the work would be done.} */
      if (++m % 100 == 0)
        @{
          putchar ('.');
          fflush (stdout);
        @}

      /* @r{Regularly the @var{expire} variable must be checked.} */
      if (expired)
        @{
          /* @r{We do not want the program to run forever.} */
          if (++switches == 20)
            return;

          printf ("\nswitching from %d to %d\n", n, 3 - n);
          expired = 0;
          /* @r{Switch to the other context, saving the current one.} */
          swapcontext (&uc[n], &uc[3 - n]);
        @}
    @}
@}

/* @r{This is the signal handler which simply set the variable.} */
void
handler (int signal)
@{
  expired = 1;
@}


int
main (void)
@{
  struct sigaction sa;
  struct itimerval it;
  char st1[8192];
  char st2[8192];

  /* @r{Initialize the data structures for the interval timer.} */
  sa.sa_flags = SA_RESTART;
  sigfillset (&sa.sa_mask);
  sa.sa_handler = handler;
  it.it_interval.tv_sec = 0;
  it.it_interval.tv_usec = 1;
  it.it_value = it.it_interval;

  /* @r{Install the timer and get the context we can manipulate.} */
  if (sigaction (SIGPROF, &sa, NULL) < 0
      || setitimer (ITIMER_PROF, &it, NULL) < 0
      || getcontext (&uc[1]) == -1
      || getcontext (&uc[2]) == -1)
    abort ();

  /* @r{Create a context with a separate stack which causes the
     function @code{f} to be call with the parameter @code{1}.
     Note that the @code{uc_link} points to the main context
     which will cause the program to terminate once the function
     return.} */
  uc[1].uc_link = &uc[0];
  uc[1].uc_stack.ss_sp = st1;
  uc[1].uc_stack.ss_size = sizeof st1;
  makecontext (&uc[1], (void (*) (void)) f, 1, 1);

  /* @r{Similarly, but @code{2} is passed as the parameter to @code{f}.} */
  uc[2].uc_link = &uc[0];
  uc[2].uc_stack.ss_sp = st2;
  uc[2].uc_stack.ss_size = sizeof st2;
  makecontext (&uc[2], (void (*) (void)) f, 1, 2);

  /* @r{Start running.} */
  swapcontext (&uc[0], &uc[1]);
  putchar ('\n');

  return 0;
@}
