user-level thread code from http://homepage.cs.uiowa.edu/~jones/opsys/threads/
This commit is contained in:
		
							parent
							
								
									fcdc22b861
								
							
						
					
					
						commit
						a0999008e0
					
				
					 5 changed files with 598 additions and 1 deletions
				
			
		
							
								
								
									
										2
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							| 
						 | 
					@ -1,5 +1,6 @@
 | 
				
			||||||
*~
 | 
					*~
 | 
				
			||||||
.*.swp
 | 
					.*.swp
 | 
				
			||||||
 | 
					*.o
 | 
				
			||||||
approximate-pi
 | 
					approximate-pi
 | 
				
			||||||
linked-list
 | 
					linked-list
 | 
				
			||||||
mandelbrot
 | 
					mandelbrot
 | 
				
			||||||
| 
						 | 
					@ -19,3 +20,4 @@ lua-foo
 | 
				
			||||||
binsearch
 | 
					binsearch
 | 
				
			||||||
test-inline-assembly
 | 
					test-inline-assembly
 | 
				
			||||||
test-inline-assembly.s
 | 
					test-inline-assembly.s
 | 
				
			||||||
 | 
					uiowa-threads-example
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										5
									
								
								Makefile
									
										
									
									
									
								
							
							
						
						
									
										5
									
								
								Makefile
									
										
									
									
									
								
							| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
CFLAGS=-std=c99 -Wall -g -O2
 | 
					CFLAGS=-std=c99 -Wall -g -O2
 | 
				
			||||||
INDENTOPTS=-kr --no-tabs --braces-on-func-def-line --indent-level2
 | 
					INDENTOPTS=-kr --no-tabs --braces-on-func-def-line --indent-level2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TARGETS=approximate-pi linked-list mandelbrot threads circular-buffer structs ncurses-pong bit-fuckery bit-fuckery2 checkcheck multibrot bloom wo-lernen lua-foo binsearch test-inline-assembly
 | 
					TARGETS=approximate-pi linked-list mandelbrot threads circular-buffer structs ncurses-pong bit-fuckery bit-fuckery2 checkcheck multibrot bloom wo-lernen lua-foo binsearch test-inline-assembly uiowa-threads-example
 | 
				
			||||||
EXTRAS=mandelbrot.bmp multibrot.png test-inline-assembly.s
 | 
					EXTRAS=mandelbrot.bmp multibrot.png test-inline-assembly.s
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.PHONY: all
 | 
					.PHONY: all
 | 
				
			||||||
| 
						 | 
					@ -40,3 +40,6 @@ test-inline-assembly: test-inline-assembly.c
 | 
				
			||||||
 | 
					
 | 
				
			||||||
test-inline-assembly.s: test-inline-assembly.c
 | 
					test-inline-assembly.s: test-inline-assembly.c
 | 
				
			||||||
	$(CC) -Wall -S -o $@ $<
 | 
						$(CC) -Wall -S -o $@ $<
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uiowa-threads-example: uiowa-threads.o uiowa-threads-example.c
 | 
				
			||||||
 | 
						$(CC) $(CFLAGS) -o $@ $^
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										29
									
								
								uiowa-threads-example.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								uiowa-threads-example.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,29 @@
 | 
				
			||||||
 | 
					/****
 | 
				
			||||||
 | 
					 * file example.c
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					   Program to demonstrate the thread management package *
 | 
				
			||||||
 | 
					   Written by Douglas Jones, Feb 18, 1998               *
 | 
				
			||||||
 | 
					                                                     ****/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <stdio.h>
 | 
				
			||||||
 | 
					#include "uiowa-threads.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void test_thread( int n )
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						printf( "A%d\n", n );
 | 
				
			||||||
 | 
						if (n > 1) thread_launch( 4000, test_thread, n-1 );
 | 
				
			||||||
 | 
						thread_relinquish();
 | 
				
			||||||
 | 
						printf( "B%d\n", n );
 | 
				
			||||||
 | 
						thread_relinquish();
 | 
				
			||||||
 | 
						printf( "C%d\n", n );
 | 
				
			||||||
 | 
						thread_relinquish();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int main()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						thread_manager_init();
 | 
				
			||||||
 | 
						thread_startup_report(); /***********/
 | 
				
			||||||
 | 
						thread_launch( 4000, test_thread, 5 );
 | 
				
			||||||
 | 
						thread_manager_start();
 | 
				
			||||||
 | 
						/* control never reaches this point */
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										486
									
								
								uiowa-threads.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										486
									
								
								uiowa-threads.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,486 @@
 | 
				
			||||||
 | 
					/****
 | 
				
			||||||
 | 
					 * file threads.c
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					   A partial implementation of a user-level thread package  
 | 
				
			||||||
 | 
					   Written by Douglas Jones,                  Feb 17, 1998
 | 
				
			||||||
 | 
					   Updated with minor bug fix in thread_exit, Feb 19, 1998
 | 
				
			||||||
 | 
					   Updated so free called on original stack,  Feb 19, 2002
 | 
				
			||||||
 | 
					   Updated with defines for _longjmp,         Aug 13, 2002
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Copyright Douglas Jones, 1998.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Permission is hereby granted to make and modify
 | 
				
			||||||
 | 
					   personal copies of this code for noncommercial or
 | 
				
			||||||
 | 
					   educational use.
 | 
				
			||||||
 | 
					                                                           *
 | 
				
			||||||
 | 
					   All other rights are reserved!                          *
 | 
				
			||||||
 | 
					                                                        ****/
 | 
				
			||||||
 | 
					#include <stdio.h>
 | 
				
			||||||
 | 
					#include <stdlib.h>
 | 
				
			||||||
 | 
					#include <setjmp.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* just in case this isn't compiled on a UNIX system
 | 
				
			||||||
 | 
					   we back off to use the standard setjmp and longjmp.
 | 
				
			||||||
 | 
					   UNIX systems have _setjmp that just does control
 | 
				
			||||||
 | 
					   transfers, and then setjmp (no underscore) that also
 | 
				
			||||||
 | 
					   saves the signal masks.  Saving those makes sense
 | 
				
			||||||
 | 
					   for exception handlers, but not for coroutines.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					#ifndef _setjmp
 | 
				
			||||||
 | 
					#define _setjmp setjmp
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef _longjmp
 | 
				
			||||||
 | 
					#define _longjmp longjmp
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/****
 | 
				
			||||||
 | 
					 * PART I: -- Reverse Engineer the jump buffer     *
 | 
				
			||||||
 | 
					 * This is the key to user-level context switching *
 | 
				
			||||||
 | 
					                                                ****/
 | 
				
			||||||
 | 
					/****                                                         *
 | 
				
			||||||
 | 
					 *  tools used by studyit to understand jump buffer structure * 
 | 
				
			||||||
 | 
					 *                                                         ****/
 | 
				
			||||||
 | 
					static int * low_bound;		/* below probe on stack */
 | 
				
			||||||
 | 
					static int * probe_local;	/* local to probe on stack */
 | 
				
			||||||
 | 
					static int * high_bound;	/* above probe on stack */
 | 
				
			||||||
 | 
					static int * prior_local;	/* value of probe_local from earlier call */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static jmp_buf probe_env;	/* saved environment of probe */
 | 
				
			||||||
 | 
					static jmp_buf probe_sameAR;	/* second environment saved by same call */
 | 
				
			||||||
 | 
					static jmp_buf probe_samePC;	/* environment saved on previous call */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static jmp_buf * ref_probe = &probe_samePC;	/* switches between probes */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/****
 | 
				
			||||||
 | 
					 * reports from studyit giving results of study
 | 
				
			||||||
 | 
					 * -- our focus here is on learning how to make
 | 
				
			||||||
 | 
					   a jump buffer that points to newly allocated *
 | 
				
			||||||
 | 
					   activation record on the heap                *
 | 
				
			||||||
 | 
					                                             ****/
 | 
				
			||||||
 | 
					static int stack_direction;	/* +1 if stack grows up, -1 if down */
 | 
				
			||||||
 | 
					long int local_offset;		/* lowest location in AR from local vars */
 | 
				
			||||||
 | 
					jmp_buf probe_record;  		/* nonzero entries mean fields that change */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/****
 | 
				
			||||||
 | 
					 * code to explore the activation record *
 | 
				
			||||||
 | 
					 * and jump buffer structure             *       
 | 
				
			||||||
 | 
					                                      ****/
 | 
				
			||||||
 | 
					void boundhigh()
 | 
				
			||||||
 | 
					/* purpose:  set high_bound to a local variable beyond probe on stack */
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int c;
 | 
				
			||||||
 | 
						high_bound = &c;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void probe()
 | 
				
			||||||
 | 
					/* purpose:  probe the activation record and jump buffer structure */
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int c;
 | 
				
			||||||
 | 
						prior_local = probe_local;
 | 
				
			||||||
 | 
						probe_local = &c;
 | 
				
			||||||
 | 
						_setjmp( *ref_probe );
 | 
				
			||||||
 | 
						ref_probe = &probe_env;
 | 
				
			||||||
 | 
						_setjmp( probe_sameAR );
 | 
				
			||||||
 | 
						(void) boundhigh();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void boundlow()
 | 
				
			||||||
 | 
					/* purpose:  set low_bound to a local variable before probe on stack */
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int c;
 | 
				
			||||||
 | 
						low_bound = &c;
 | 
				
			||||||
 | 
						(void) probe();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void fill()
 | 
				
			||||||
 | 
					/* purpose:  put some padding on the stack and then study the result */
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						(void) boundlow();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void studyit()
 | 
				
			||||||
 | 
					/* purpose:  study the result of probes */
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (sizeof(long int) != sizeof(long int *)) {
 | 
				
			||||||
 | 
							fprintf( stderr, "Pointer size is wrong\n" );
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (((long int)high_bound) > ((long int)low_bound)) {
 | 
				
			||||||
 | 
							/* stack grows up */
 | 
				
			||||||
 | 
							stack_direction =  1;
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							/* stack grows down */
 | 
				
			||||||
 | 
							stack_direction = -1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* study the jump buffer */
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							int i;
 | 
				
			||||||
 | 
							long int * p;
 | 
				
			||||||
 | 
							long int * sameAR;
 | 
				
			||||||
 | 
							long int * samePC;
 | 
				
			||||||
 | 
							long int * probe_rec;
 | 
				
			||||||
 | 
							long int prior_diff = (long int)probe_local
 | 
				
			||||||
 | 
										- (long int)prior_local;
 | 
				
			||||||
 | 
							long int min_frame = (long int)probe_local;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* following line views jump buffer as array of long int */
 | 
				
			||||||
 | 
							p = (long int *)probe_env;
 | 
				
			||||||
 | 
							sameAR = (long int *)probe_sameAR;
 | 
				
			||||||
 | 
							samePC = (long int *)probe_samePC;
 | 
				
			||||||
 | 
							probe_rec = (long int *)probe_record;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for (i = 0; i < sizeof(jmp_buf); i += sizeof(long int) ) {
 | 
				
			||||||
 | 
								*probe_rec = 0;
 | 
				
			||||||
 | 
								if (*p != *samePC) {
 | 
				
			||||||
 | 
									if (*p != *sameAR) {
 | 
				
			||||||
 | 
										fprintf( stderr, "No Thread Launch\n" );
 | 
				
			||||||
 | 
										exit(-1);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if ((*p - *samePC) == prior_diff) {
 | 
				
			||||||
 | 
										/* record frame dependency */
 | 
				
			||||||
 | 
										*probe_rec = 1;
 | 
				
			||||||
 | 
										if (stack_direction == 1) { /* up */
 | 
				
			||||||
 | 
											if (min_frame > *p) {
 | 
				
			||||||
 | 
												min_frame = *p;
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										} else { /* grows down */
 | 
				
			||||||
 | 
											if (min_frame < *p) {
 | 
				
			||||||
 | 
												min_frame = *p;
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					 			
 | 
				
			||||||
 | 
								p++, sameAR++, samePC++, probe_rec++;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (stack_direction == 1) { /* grows up */
 | 
				
			||||||
 | 
								local_offset
 | 
				
			||||||
 | 
									= (long int)probe_local - min_frame;
 | 
				
			||||||
 | 
							} else { /* stack grows down */
 | 
				
			||||||
 | 
								local_offset
 | 
				
			||||||
 | 
									= min_frame - (long int)probe_local;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void thread_startup_report()
 | 
				
			||||||
 | 
					/* reports on the results of the study done by above code */
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int i;
 | 
				
			||||||
 | 
						long int * jb;
 | 
				
			||||||
 | 
						printf( "Thread startup study results\n" );
 | 
				
			||||||
 | 
						if (stack_direction == 1) {
 | 
				
			||||||
 | 
							printf( "  Stack grows up\n" );
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							printf( "  Stack grows down\n" );
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						printf( "  Local variable offset from AR base = %1d\n", local_offset );
 | 
				
			||||||
 | 
						printf( "  Jump buffer fields subject to modification:\n" );
 | 
				
			||||||
 | 
						jb = (long int *)probe_record;
 | 
				
			||||||
 | 
						for (i = 0; i < sizeof(jmp_buf); i += sizeof(long int) ) {
 | 
				
			||||||
 | 
							if (*jb != 0) {
 | 
				
			||||||
 | 
								printf( "    %1d\n", i );
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							jb++;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						printf( "  Jump buffer size: %1d\n", sizeof(jmp_buf) );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/****
 | 
				
			||||||
 | 
					 * PART II: -- The Thread Manager          *
 | 
				
			||||||
 | 
					 * Based on the results reported by Part I *
 | 
				
			||||||
 | 
					                                        ****/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/****                *
 | 
				
			||||||
 | 
					 * thread data types * 
 | 
				
			||||||
 | 
					 *                ****/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct thread {
 | 
				
			||||||
 | 
						struct thread * next;	/* used to link threads into queues */
 | 
				
			||||||
 | 
						int size;		/* the size of the thread */
 | 
				
			||||||
 | 
						void (* proc)(int);	/* procedure for body of thread */
 | 
				
			||||||
 | 
						int param;		/* parameter to base procedure */
 | 
				
			||||||
 | 
						jmp_buf state;		/* the state of the thread */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* size bytes of stack follow here */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define thread_null (struct thread *)0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct thread_queue {
 | 
				
			||||||
 | 
						struct thread * head;
 | 
				
			||||||
 | 
						struct thread * tail;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/****                                    *
 | 
				
			||||||
 | 
					 * Fundamental scheduler data structures *
 | 
				
			||||||
 | 
					 *                                    ****/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct thread_queue readylist;  /* the list of all ready threads */
 | 
				
			||||||
 | 
					static struct thread * current;        /* the current running thread */
 | 
				
			||||||
 | 
					static void * mem_to_free;             /* anything to be passed to free() */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static char * thread_error;            /* string giving package death cause */
 | 
				
			||||||
 | 
					static jmp_buf thread_death;           /* used on thread package death */
 | 
				
			||||||
 | 
					static jmp_buf go_free_it;             /* used to call free from original stk */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/****                              *
 | 
				
			||||||
 | 
					 * code for thread_queue data type * 
 | 
				
			||||||
 | 
					 *                              ****/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void thread_queue_init( struct thread_queue * q )
 | 
				
			||||||
 | 
					/* initialize q */
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						q->head = thread_null;
 | 
				
			||||||
 | 
						q->tail = thread_null;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void thread_enqueue( struct thread * t, struct thread_queue * q )
 | 
				
			||||||
 | 
					/* enqueue t on q */
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						t->next = thread_null;
 | 
				
			||||||
 | 
						if (q->head == thread_null) {
 | 
				
			||||||
 | 
							q->head = t;
 | 
				
			||||||
 | 
							q->tail = t;
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							q->tail->next = t;
 | 
				
			||||||
 | 
							q->tail = t;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct thread * thread_dequeue( struct thread_queue * q )
 | 
				
			||||||
 | 
					/* dequeue and return a thread from q */
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (q->head == thread_null) {
 | 
				
			||||||
 | 
							return thread_null;
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							struct thread * t;
 | 
				
			||||||
 | 
							t = q->head;
 | 
				
			||||||
 | 
							q->head = t->next;
 | 
				
			||||||
 | 
							return t;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/****                                       *
 | 
				
			||||||
 | 
					 * user callable thread management routines * 
 | 
				
			||||||
 | 
					 *                                       ****/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void thread_manager_init()
 | 
				
			||||||
 | 
					/* call this once before launching any threads */
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/* first study current system and see if thread launch worth trying */
 | 
				
			||||||
 | 
						(void) fill();		/* do a probe with filler on stack */
 | 
				
			||||||
 | 
						(void) boundlow();	/* do a probe without filler */
 | 
				
			||||||
 | 
						(void) studyit();	/* figure out what it all means */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* now build thread manager data structures */
 | 
				
			||||||
 | 
						thread_queue_init( &readylist );
 | 
				
			||||||
 | 
						mem_to_free = (void *)0;
 | 
				
			||||||
 | 
						current = thread_null;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void thread_manager_start()
 | 
				
			||||||
 | 
					/* call this once after launching at least one thread */
 | 
				
			||||||
 | 
					/* this only returns when all threads are blocked! */
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						current = thread_dequeue( &readylist );
 | 
				
			||||||
 | 
						if (current == thread_null) {
 | 
				
			||||||
 | 
							/* crisis */
 | 
				
			||||||
 | 
							fprintf(stderr, "Thread manager start failure, no threads!\n");
 | 
				
			||||||
 | 
							exit(-1);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (_setjmp( thread_death )) {
 | 
				
			||||||
 | 
							/* comes here when _longjmp( thread_death, 1 ) done */
 | 
				
			||||||
 | 
							fprintf(stderr,
 | 
				
			||||||
 | 
								"Thread manager terminated, %s!\n", thread_error );
 | 
				
			||||||
 | 
							if (mem_to_free != (void *)0 ) {
 | 
				
			||||||
 | 
								/* see comments below */
 | 
				
			||||||
 | 
								free( mem_to_free );
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							/* we will come back here whenever we need to deallocate */
 | 
				
			||||||
 | 
							(void) _setjmp( go_free_it );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (mem_to_free != (void *)0 ) {
 | 
				
			||||||
 | 
								/* it's not safe to call free() anywhere but on the
 | 
				
			||||||
 | 
								   real stack, so once the thread manager is running,
 | 
				
			||||||
 | 
								   this is the only safe place to call it!
 | 
				
			||||||
 | 
							        */
 | 
				
			||||||
 | 
								free( mem_to_free );
 | 
				
			||||||
 | 
								mem_to_free = (void *)0;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							_longjmp( current->state, 1 );
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void thread_relinquish()
 | 
				
			||||||
 | 
					/* call this within a thread to allow scheduling of a new thread */
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (_setjmp( current->state ) == 0) {
 | 
				
			||||||
 | 
							thread_enqueue( current, &readylist );
 | 
				
			||||||
 | 
							current = thread_dequeue( &readylist );
 | 
				
			||||||
 | 
							_longjmp( current->state, 1 );
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void thread_exit()
 | 
				
			||||||
 | 
					/* call this within a thread to terminate that thread */
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/* we can't deallocate, so we schedule this thread for deallocation */
 | 
				
			||||||
 | 
						mem_to_free = (void *)current;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* now, get the next thread to run */
 | 
				
			||||||
 | 
						current = thread_dequeue( &readylist );
 | 
				
			||||||
 | 
						if (current == thread_null) {
 | 
				
			||||||
 | 
							/* crisis */
 | 
				
			||||||
 | 
							thread_error = "ready list empty";
 | 
				
			||||||
 | 
							_longjmp( thread_death, 1 );
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_longjmp( go_free_it, 1 );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void thread_launch( int size, void (* proc)(int), int param )
 | 
				
			||||||
 | 
					/* call this to launch proc(param) as a thread */
 | 
				
			||||||
 | 
					/* may be called from main or from any thread */
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct thread * t;
 | 
				
			||||||
 | 
						t = (struct thread *)malloc( sizeof(struct thread) + size );
 | 
				
			||||||
 | 
						t->size = size;
 | 
				
			||||||
 | 
						t->proc = proc;
 | 
				
			||||||
 | 
						t->param = param;
 | 
				
			||||||
 | 
						if (_setjmp( t->state )) {
 | 
				
			||||||
 | 
							/* comes here only when new thread scheduled first time */
 | 
				
			||||||
 | 
							(*current->proc)(current->param);
 | 
				
			||||||
 | 
							thread_exit();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						/* continue initialization */
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							long int * s;
 | 
				
			||||||
 | 
							long int * probe_rec;
 | 
				
			||||||
 | 
							long int local_base = (long int)&t; /* address of local t */
 | 
				
			||||||
 | 
							long int new_base;
 | 
				
			||||||
 | 
							int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* the following code copies the contents of the
 | 
				
			||||||
 | 
							   activation record, just in case it might prove
 | 
				
			||||||
 | 
							   useful; note that we don't know that this is
 | 
				
			||||||
 | 
							   needed on any machine, but it might, so we do it. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (stack_direction == 1) { /* grows up */
 | 
				
			||||||
 | 
								long int * src;
 | 
				
			||||||
 | 
								long int * dst;
 | 
				
			||||||
 | 
								new_base = (long int)t + sizeof( struct thread )
 | 
				
			||||||
 | 
								 	+ local_offset;
 | 
				
			||||||
 | 
								src = (long int *) (local_base - local_offset);
 | 
				
			||||||
 | 
								dst = (long int *) (new_base - local_offset);
 | 
				
			||||||
 | 
								for (i = 0; i <= local_offset; i += sizeof(long int)) {
 | 
				
			||||||
 | 
									*dst++ = *src++;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} else { /* grows down */
 | 
				
			||||||
 | 
								long int * src;
 | 
				
			||||||
 | 
								long int * dst;
 | 
				
			||||||
 | 
								new_base = (long int)t + sizeof( struct thread )
 | 
				
			||||||
 | 
								 	+ size - (local_offset + sizeof(long int));
 | 
				
			||||||
 | 
								src = (long int *) (local_base);
 | 
				
			||||||
 | 
								dst = (long int *) (new_base);
 | 
				
			||||||
 | 
								for (i = 0; i <= local_offset; i += sizeof(long int)) {
 | 
				
			||||||
 | 
									*dst++ = *src++;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* the following code adjusts the references to the
 | 
				
			||||||
 | 
							   activation record in the saved thread state so that
 | 
				
			||||||
 | 
							   they point to the base of the newly allocated stack */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							s = (long int *)(t->state);
 | 
				
			||||||
 | 
							probe_rec = (long int *)probe_record;
 | 
				
			||||||
 | 
							for (i = 0; i < sizeof(jmp_buf); i += sizeof(long int) ) {
 | 
				
			||||||
 | 
								if (*probe_rec != 0) {
 | 
				
			||||||
 | 
									/* adjust this field of state */
 | 
				
			||||||
 | 
									*s += new_base - local_base;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								s++;
 | 
				
			||||||
 | 
								probe_rec++;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						thread_enqueue( t, &readylist );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void thread_free( void * ptr )
 | 
				
			||||||
 | 
					/* call this from within threads instead of free, because some
 | 
				
			||||||
 | 
					   heap managers are paranoid enough to forbid a call to free
 | 
				
			||||||
 | 
					   when the stack itself is in a block allocated in the heap */
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						mem_to_free = ptr;
 | 
				
			||||||
 | 
						if (_setjmp( current->state ) == 0) {
 | 
				
			||||||
 | 
							_longjmp( go_free_it, 1 );
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						/* after call to free(), there will be a longjmp back to here */
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void thread_safety_check()
 | 
				
			||||||
 | 
					/* call this to check for thread stack overflow */
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						long int t = (long int)current + sizeof(struct thread);
 | 
				
			||||||
 | 
						if ( ((long int)&t < t) || ((long int)&t > (t + current->size)) ) {
 | 
				
			||||||
 | 
							/* crisis */
 | 
				
			||||||
 | 
							thread_error = "thread stack overflow";
 | 
				
			||||||
 | 
							_longjmp( thread_death, 1 );
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/****                                      *
 | 
				
			||||||
 | 
					 * thread manager extension for semaphores *
 | 
				
			||||||
 | 
					 *                                      ****/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* semaphore representation known only to semaphore methods */
 | 
				
			||||||
 | 
					struct thread_semaphore {
 | 
				
			||||||
 | 
						int count;
 | 
				
			||||||
 | 
					        struct thread_queue queue;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* methods applying to semaphores */
 | 
				
			||||||
 | 
					void thread_semaphore_init( struct thread_semaphore * s, int v )
 | 
				
			||||||
 | 
					/* call this to initialize semaphore s to a count of v */
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						s->count = v;
 | 
				
			||||||
 | 
					        thread_queue_init( &s->queue );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void thread_wait( struct thread_semaphore * s )
 | 
				
			||||||
 | 
					/* call this within a thread to block on a semaphore */
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (s->count > 0) {
 | 
				
			||||||
 | 
							s->count--;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
							if (_setjmp( current->state ) == 0) {
 | 
				
			||||||
 | 
								thread_enqueue( current, &s->queue );
 | 
				
			||||||
 | 
								current = thread_dequeue( &readylist );
 | 
				
			||||||
 | 
								if (current == thread_null) {
 | 
				
			||||||
 | 
									/* crisis */
 | 
				
			||||||
 | 
									thread_error = "possible deadlock";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									_longjmp( thread_death, 1 );
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								_longjmp( current->state, 1 );
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void thread_signal( struct thread_semaphore * s )
 | 
				
			||||||
 | 
					/* call this within a thread to signal a semaphore */
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct thread * t = thread_dequeue( &s->queue );
 | 
				
			||||||
 | 
						if (t == thread_null) {
 | 
				
			||||||
 | 
							s->count++;
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							thread_enqueue( t, &readylist );
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										77
									
								
								uiowa-threads.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								uiowa-threads.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,77 @@
 | 
				
			||||||
 | 
					/****
 | 
				
			||||||
 | 
					 * file threads.h
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					   Header files for the user-level thread package  
 | 
				
			||||||
 | 
					   Written by Douglas Jones,    Feb 18, 1998
 | 
				
			||||||
 | 
					   Added thread_free interface, Feb 19, 2002
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Copyright Douglas Jones, 1998.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Permission is hereby granted to make and modify
 | 
				
			||||||
 | 
					   personal copies of this code for noncommercial or
 | 
				
			||||||
 | 
					   educational use.
 | 
				
			||||||
 | 
					                                                     *
 | 
				
			||||||
 | 
					   All other rights are reserved!                    *
 | 
				
			||||||
 | 
					                                                  ****/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/****                      *
 | 
				
			||||||
 | 
					 * Initialization routines *
 | 
				
			||||||
 | 
					 *                      ****/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void thread_manager_init();
 | 
				
			||||||
 | 
					/* call this once before launching any threads */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void thread_launch( int size, void (* proc)(int), int param );
 | 
				
			||||||
 | 
					/* call this to launch proc(param) as a thread */
 | 
				
			||||||
 | 
					/* may be called from main or from any thread */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void thread_manager_start();
 | 
				
			||||||
 | 
					/* call this once after launching at least one thread */
 | 
				
			||||||
 | 
					/* this only returns when all threads are blocked! */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/****                                    *
 | 
				
			||||||
 | 
					 * Routines to call from within a thread *
 | 
				
			||||||
 | 
					 *                                    ****/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void thread_relinquish();
 | 
				
			||||||
 | 
					/* call this within a thread to allow scheduling of a new thread */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void thread_exit();
 | 
				
			||||||
 | 
					/* call this within a thread to terminate that thread */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void thread_free( void * ptr );
 | 
				
			||||||
 | 
					/* call this within a thread instead of calling free() */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* note, thread_launch() may also be called within a thread */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/****                             *
 | 
				
			||||||
 | 
					 * Utility and debugging routines *
 | 
				
			||||||
 | 
					 *                             ****/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void thread_startup_report();
 | 
				
			||||||
 | 
					/* reports on the results of the study done in the startup */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void thread_safety_check();
 | 
				
			||||||
 | 
					/* call this to check for thread stack overflow */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/****                                    *
 | 
				
			||||||
 | 
					 * Semaphore extension to thread package *
 | 
				
			||||||
 | 
					 *                                    ****/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* the declaration of the semaphore type given here is a lie! */
 | 
				
			||||||
 | 
					struct thread_semaphore {
 | 
				
			||||||
 | 
						void * unused_fields[3];
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void thread_semaphore_init( struct thread_semaphore * s, int v );
 | 
				
			||||||
 | 
					/* call this to initialize a semaphore */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void thread_wait( struct thread_semaphore * s );
 | 
				
			||||||
 | 
					/* call this within a thread to block on a semaphore */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void thread_signal( struct thread_semaphore * s );
 | 
				
			||||||
 | 
					/* call this within a thread to signal a semaphore */
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue