/**** * 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); } }