/* context.c */

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

#if 0

/* Here's the beginnings of a simple evaluation engine.  I may complete it, so
 * that I can see how get/setcontext() can be used in Tcl. */

typedef enum variable_type {
    VT_INTEGER,
    VT_BOOLEAN
} variable_type_t;

typedef struct variable {
    char* name;
    variable_type_t type;

    union {
        struct integer {
            int val;
        };
        struct boolean {
            int val;
        };
    } data;
} variable_t;

typedef enum operation {
    OP_ADD, OP_SUB, OP_MUL, OP_DIV, OP_MOD, OP_NEG,
    OP_EQ , OP_NE , OP_GT , OP_GE , OP_LT , OP_LE ,
    OP_AND, OP_IOR, OP_XOR, OP_NOT, OP_IF , OP_SET
} operation_t;

typedef enum node_type {
    NT_OPERATION,
    NT_INTEGER  ,
    NT_BOOLEAN  ,
    NT_VARIABLE
} node_type_t;

struct node; typedef struct node node_t;
struct node {
    node_type_t type;

    union {
        struct operation {
            operation_t op;
            node_t* args[3];
        };
        struct integer {
            int val;
        };
        struct boolean {
            int val;
        };
        struct variable {
            variable_t* var;
        };
    } data;
}

typedef struct thread {
    variable_t* variables;
    int variables_num, variables_cap;

    node_t* tree;
} thread_t;

#endif

static int sem;
static ucontext_t bravo_uc;
static ucontext_t delta_uc;
static ucontext_t main_uc ;

static void alpha  (void);
static void bravo  (void);
static void charlie(void);
static void delta  (void);

static void alpha(void)
{
    printf("alpha  : entering\n");
    bravo();
    printf("alpha  : returning\n");
}

static void bravo(void)
{
    static int count = 0;

    printf("bravo  : entering\n");

    if (getcontext(&bravo_uc) != 0) {
        perror("getcontext");
        exit(EXIT_FAILURE);
    }

    if (sem) {
        printf("bravo  : resuming\n");
    }

    ++count;
    printf("bravo  : count = %d\n", count);

    if (sem) {
        sem = 0;
        printf("bravo  : leaving\n");
        setcontext(&main_uc);
    }

    printf("bravo  : returning\n");
}

static void charlie(void)
{
    printf("charlie: entering\n");
    delta();
    printf("charlie: returning\n");
}

static void delta(void)
{
    static int count = 0;

    printf("delta  : entering\n");

    if (getcontext(&delta_uc) != 0) {
        perror("getcontext");
        exit(EXIT_FAILURE);
    }

    if (sem) {
        printf("delta  : resuming\n");
    }

    ++count;
    printf("delta  : count = %d\n", count);

    if (sem) {
        sem = 0;
        printf("delta  : leaving\n");
        setcontext(&main_uc);
    }

    printf("delta  : returning\n");
}

int main(void)
{
    printf("main   : entering\n");

    alpha  ();
    charlie();

    printf("main   : beginning loop\n");

    while (1) {
        printf("main   : b)ravo; d)elta; q)uit --> ");
        fflush(stdout);

again:  switch (getchar()) {
        case 'b':
            sem = 1;
            getcontext(&main_uc);
            if (sem) {
                setcontext(&bravo_uc);
            } else {
                printf("main   : resuming\n");
            }
            break;
        case 'd':
            sem = 1;
            getcontext(&main_uc);
            if (sem) {
                setcontext(&delta_uc);
            } else {
                printf("main   : resuming\n");
            }
            break;
        case 'q':
            printf("main   : terminating\n");
            exit(EXIT_SUCCESS);
            break;
        default:
            goto again;
        }
    }
}

/* vim: set ts=4 sts=4 sw=4 tw=80 et: */

