/****
 * Example 3: Dynamic Process Creation and Destruction
 ****/

#define XMEM_ADDR unsigned long

typedef struct Thread {
    int id;
    CoData cd;
    XMEM_ADDR next;
} Thread;

int numthreads;                     // number of threads running
int nextid;                         // next available thread ID
XMEM_ADDR firstfree;                // first free thread structure
XMEM_ADDR firstthread;              // first active thread

/****
 * newthread()
 *
 * Creates a new thread of the specified type and adds it to the
 * 'active' queue.  If there are any pre-allocated thread structures
 * left over from threads that we've killed, we'll grab on of those
 * and use it.  Otherwise we'll xalloc a new thread structure.
 ****/
void newthread() {
    XMEM_ADDR addr;
    Thread t;

#GLOBAL_INIT {
    numthreads = 0;
    nextid = 0;
    firstthread = 0;
    firstfree = 0;
}

    if (firstfree == 0) {
        // No free thread structures available; xalloc one
        addr = xalloc(sizeof(Thread));
        if (addr == 0) {
            printf("ERROR: Can not allocate any more threads (out of xmem)\n");
            return;
        }
    } else {
        // Grab the first free thread structure from the free list and use that
        // for our new thread.
        addr = firstfree;
        xmem2root(&t, addr, sizeof(Thread));
        firstfree = t.next;
    }

    // Update the thread information structure
    t.id = ++nextid;
    t.next = firstthread;
    CoBegin(&t.cd);
    root2xmem(addr, &t, sizeof(Thread));
    firstthread = addr;
    ++numthreads;
    printf("Creating thread #%d; there are now %d running.\n",
            nextid, numthreads);
}


/****
 * killthread()
 *
 * Kill the first thread on the active queue.  Since Dynamic C does not
 * allow the de-allocation of xalloc'd memory, the best we can do is put
 * it on a freelist so that it can be reused later.
 *
 * For simplicity, this just kills the first thread on the active queue
 * (i.e. the last thread created) and runs in O(1).  It would be possible 
 * to kill a specific thread by ID, by performing a linear search on the 
 * linked list; this would make thread destruction O(n).
 *
 * @return The address of the thread killed
 ****/
XMEM_ADDR killthread() {
    XMEM_ADDR addr;
    Thread t;

    addr = firstthread;
    if (addr == 0)
        // no active threads
        return 0;

    // Move thread from active list to free list
    xmem2root(&t, addr, sizeof(Thread));
    firstthread = t.next;
    t.next = firstfree;
    root2xmem(addr, &t, sizeof(Thread));
    firstfree = addr;
    --numthreads;
    printf("Killed a thread; there are now %d left.\n", numthreads);
    return addr;
}


main(void) {
    Thread t;

    // Address of next thread to run
    XMEM_ADDR nextthread;

    // CoData pointer for scheduling
    CoData* pcd;

    nextthread = 0;
    while(1) {
        // Keyboard handler
        costate {
            while(1) {
                waitfor(kbhit());
                switch(getchar()) {
                    /* Stop one of the currently running threads */
                    case '-':
                        /* Make sure there is an enabled thread */
                        if (firstthread == 0) {
                            printf("All threads stopped.\n");
                            break;
                        }
                        if (nextthread == killthread())
                            nextthread = firstthread;
                        break;

                    case '+':
                        newthread();
                        break;
                }
            }
        }

        /* Task selection code */
        if (nextthread == 0 && (nextthread = firstthread) == 0)
            continue;

        xmem2root(&t, nextthread, sizeof(Thread));
        pcd = &t.cd;

        /* Task execution */
        costate pcd {
            // In this example, all threads are identical.  If we wanted,
            // we could switch on the thread ID to run different code
            // or we could add a task type field to the thread structure
            // that would be used here to determine what to do.
            while(1) {
                printf("Thread #%d running.\n", t.id);
                yield;
            }
        }

        /* Select the next task to run */
        (nextthread = t.next) || (nextthread = firstthread);
    }
}

