_LIGHTWEIGHT TASKS IN C_ by Jonathan Finger Listing One /* Lightweight Multitasker in C -- by Jonathan Finger, 1995 (reformatted) */ #include #include #include #include #include #include #include #define MAX_THREAD 10 #define THREAD_SWAP_STACK_SIZE 10000 typedef short int THREAD_NUM; typedef unsigned char UCHAR; typedef unsigned long ULONG; typedef unsigned int UINT; typedef unsigned short int USHORT; typedef unsigned char * STR; /*----------------------the threads structure-------------------------------*/ struct threads { THREAD_NUM thread_number; jmp_buf swap_thread_buff; UCHAR c_stack[THREAD_SWAP_STACK_SIZE];/* save area for c stack */ size_t c_stack_size; /* save state info */ THREAD_NUM volatile next_thread; /* forward chain for queues */ THREAD_NUM volatile prev_thread; /* backward chain for some queues*/ UCHAR volatile queue; /* current queue that thread is */ /* on: 'R'means RUN, 'F'is Free */ void (*function)(void); /* other info can be added here as needed */ }; typedef struct threads thrd; thrd thread[MAX_THREAD]; thrd *cpr; /* pointer to the current running thread */ /*------------------------------------------------------------------------*/ jmp_buf new_thread_start_buff; #define NO_THREAD (MAX_THREAD+100) THREAD_NUM current_thread = NO_THREAD; STR stack_swap_start; /* note that the following variables have been declared volatile, since * they may be altered by an interrupt service routine */ static THREAD_NUM volatile run_queue_head; static THREAD_NUM volatile run_queue_tail; /*------------------------Function prototypes-------------------------*/ void init_thread_table (void); int start_new_thread (void (*program)(void)); void free_current_thread (void); void setup_thread_globals (THREAD_NUM thread_no, STR buff); void save_thread_globals (void); int swap_out_thread (void); void swap_in_thread (void); void swap_thread (void); void swap_thread_block (void); /*put curr thread to sleep on event*/ void queue_thread (THREAD_NUM thread_number); void unqueue_thread (UCHAR new_queue); void thread1(void) { while (1) { printf("\n\rthread 1"); swap_thread(); } } void thread2(void) { while (1) { printf("\n\rthread 2"); swap_thread(); } } void thread3(void) { while (1) { if (kbhit()) exit(0); printf("\n\rthread 3"); swap_thread(); } } main() { int i = 0; init_thread_table(); run_queue_head = run_queue_tail = NO_THREAD; stack_swap_start = (STR) &i; if (!setjmp(new_thread_start_buff)) { /* starts three threads */ start_new_thread(thread1);/*should error-check return value*/ start_new_thread(thread2); start_new_thread(thread3); swap_thread_block(); } (*(cpr->function))(); free_current_thread(); } void init_thread_table() { int i = 0; while (i < MAX_THREAD) { thread[i].thread_number = i; thread[i].queue = 'F'; thread[i].next_thread = i + 1; thread[i].prev_thread = i - 1; i++; }; thread[MAX_THREAD].next_thread = NO_THREAD; } int start_new_thread(void *(program)(void)) { THREAD_NUM thread_num; thrd *threadp; thread_num = get_free_thread_id(); if (thread_num == NO_THREAD) return(NO_THREAD); threadp = &thread[thread_num]; threadp->c_stack_size = 0; threadp->next_thread = NO_THREAD; threadp->function = program; memcpy(threadp->swap_thread_buff, new_thread_start_buff, sizeof(jmp_buf)); /* the memcpy copies the contents of new_thread_start_buff * to the thread's swap buff so that when swap_in_thread() * calls longjmp(), control returns from the setjmp in main() */ queue_thread(thread_num); return(thread_num); } static int get_free_thread_id() { int i = 0; do { while ((i < MAX_THREAD) && (thread[i].queue != 'F')) i++; if (i < MAX_THREAD) return(i); } while (i < MAX_THREAD); return(NO_THREAD); } void setup_thread_globals(THREAD_NUM thread_num, STR buff) { /*STR buff; needed to defeat optimizer */ cpr = &thread[thread_num]; } void save_thread_globals() { } int swap_out_thread() { long int i; if (current_thread == NO_THREAD) return(0); save_thread_globals(); i = stack_swap_start - ((STR)&i); cpr->c_stack_size = (size_t) i; memcpy(cpr->c_stack, ((STR)&i)+1, (size_t) i); return(setjmp(cpr->swap_thread_buff)); /* the setjmp sets the return point where the thread will * resume execution when longjmp() in swap_in_thread() */ } void swap_in_thread() { UCHAR buffer[thread_SWAP_STACK_SIZE]; /* make sure we are above (below) the swap stack */ current_thread = run_queue_head; setup_thread_globals(current_thread, &buffer[0]); memcpy(stack_swap_start - cpr->c_stack_size + 1, cpr->c_stack, cpr->c_stack_size); longjmp(cpr->swap_thread_buff, 1); /* lonjmp() transfers control back to setjmp() in swap_out_thread */ } void swap_thread() { int next_thread; next_thread = thread[run_queue_head].next_thread; if (next_thread != NO_THREAD) { run_queue_head = next_thread; thread[run_queue_tail].next_thread = current_thread; run_queue_tail = current_thread; thread[current_thread].next_thread = NO_THREAD; if (!swap_out_thread()) swap_in_thread(); /* if swap_out_thread() returns 0, this is a return from the * call to swap_out_thread and we call swap_in_thread. If it * returns !0 then swap_out_thread is returning from longjmp() * in swap_in_thread and task switch has already occurred */ } } void swap_thread_block() /*put current thread to sleep on event*/ { while (run_queue_head == NO_THREAD) continue; /* if this loop is encountered and run queue is empty, process idles * until a process is queue either in an interrupt service routine or * signal handler */ if (current_thread != run_queue_head && !swap_out_thread()) { swap_in_thread(); } } void queue_thread(THREAD_NUM thread_number) { /* If run queue can be modified by an interrupt service routine or * signal handler then code must be added to assure mutual exclusion */ if (run_queue_tail != NO_THREAD) { thread[run_queue_tail].next_thread = thread_number; } else { run_queue_head = thread_number; } run_queue_tail = thread_number; thread[thread_number].next_thread = NO_THREAD; thread[thread_number].queue = 'R'; } void unqueue_thread(UCHAR new_queue) { THREAD_NUM thread_num; thread_num = run_queue_head; if ((run_queue_head = thread[thread_num].next_thread) == NO_THREAD) run_queue_tail = NO_THREAD; thread[thread_num].queue = new_queue; } void free_current_thread() { free_thread(current_thread); unqueue_thread('F'); swap_thread_block(); /* this will never return */ } int free_thread(THREAD_NUM thread_num) { thrd *threadp = &thread[thread_num]; if (threadp->queue == 'F') return(0); if (current_thread == thread_num) current_thread = NO_THREAD; return(1); }