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
 | 
			
		||||
*.o
 | 
			
		||||
approximate-pi
 | 
			
		||||
linked-list
 | 
			
		||||
mandelbrot
 | 
			
		||||
| 
						 | 
				
			
			@ -19,3 +20,4 @@ lua-foo
 | 
			
		|||
binsearch
 | 
			
		||||
test-inline-assembly
 | 
			
		||||
test-inline-assembly.s
 | 
			
		||||
uiowa-threads-example
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										5
									
								
								Makefile
									
										
									
									
									
								
							
							
						
						
									
										5
									
								
								Makefile
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -1,7 +1,7 @@
 | 
			
		|||
CFLAGS=-std=c99 -Wall -g -O2
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
.PHONY: all
 | 
			
		||||
| 
						 | 
				
			
			@ -40,3 +40,6 @@ test-inline-assembly: test-inline-assembly.c
 | 
			
		|||
 | 
			
		||||
test-inline-assembly.s: test-inline-assembly.c
 | 
			
		||||
	$(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