Personal tools
You are here: Home Projects C++ Cfront releases Release 3.0.3 source lib task_SPARC task task.c
Document Actions

task.c

by Michael L Powell last modified 2007-01-26 03:22

Click here to get the file

Size 18.2 kB - File type text/plain

File contents

/*ident	"@(#)cop4:sparc_task/task/task.c	1.1" */

/*******************************************************************************
 
C++ source for the C++ Language System, Release 3.0.  This product
is a new release of the original cfront developed in the computer
science research center of AT&T Bell Laboratories.

Copyright (c) 1991 AT&T and UNIX System Laboratories, Inc.
Copyright (c) 1984, 1989, 1990 AT&T.  All Rights Reserved.

THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE of AT&T and UNIX System
Laboratories, Inc.  The copyright notice above does not evidence
any actual or intended publication of such source code.

*******************************************************************************/
/*
 * Copyright (c) 1990, 1991 by Sun Microsystems, Inc.
 */

#include <task.h>
#include "hw_stack.h"
#include <assert.h>

task* task::txsk_chxin;
team* task::team_to_delete;

#define ABSV(x) ( (x) > 0 ? (x) : -(x) )

const int NEW_CHILD = 1;

int _hwm;

HW_REGS New_task_regs;   /* hardware regs for activating new child
			  * in task::task frame
			  */

// a team is a collection of tasks that share one stack
class team
{
friend class task;
	int	no_of_tasks;
	task*	got_stack;
	int*	stack;
	int	size;	// of the stack
	team(task*, int =0);	// stacksize == zero ==> using the main stack
	~team() { delete stack; }
};

team::team(task* t, int stacksize) {
	no_of_tasks = 1;
	got_stack = t;
	if (size = stacksize) {
		// Sparc needs the new stack to be double-word aligned,
		// but "team::~team" will delete stack, so we will get
		// the stack aligned by setting the frame pointer
		// appropriately.
		stack = new int[stacksize];
		while (stack == 0) object::task_error(E_STORE, (object*)0);
		if (_hwm) {
			for (int x = 0; x < stacksize; x++) {
				stack[x] = UNTOUCHED;
			}
		}
	}
}


static void
usemainstack()
/* fudge to allow simple stack overflow check */
{
	int* sp = TOP();

	if (_hwm) {
		/* WARNING:  This function used to declare an array of
		 * SIZE+100 and set each element to UNTOUCHED instead
		 * of using the following method.  But then when compilers
		 * inlined this function it caused each new task's stack
		 * to overflow on initialization, because the task::task
		 * stack frame was SIZE+100 larger.
		 */

		// start with next word after "sp"
		for (int i = 2; i < SIZE+100; i++) {
			*(STACK_LAST_WORD_P(sp, i)) = UNTOUCHED;
		}
	} else {
		*(STACK_LAST_WORD_P(sp, 0)) = 0;
	}
}

void 
copy_stack(register int* f, register int c, register int* t)
/*
	copy c words down from f to t
	do NOT attempt to copy "copy_stack"'s own stackframe
*/
{
	COPY_STACK(f,c,t);
}

/* init_stack copies contents of parent stack (starting at pa_fp)
 * to child stack.  Adjusts pointers in saved ta_fp frame of child stack 
 * (to point to places in child stack).
 * Returns distance from parent stack to child stack.
 *
 * init_stack is called *only* by task::task, when creating a new task.
 */
int
task::init_stack(int* ta_fp, int* pa_fp)
{
#ifdef sparc
	//
	// On sparc, to preverve the addressible parameters that were
	// passed to the derived-task's constructor, we must copy an
	// extra stack frame.
	//
	int* in_stk = (int*) OLD_FP( pa_fp );
#else
	int* in_stk = pa_fp;
#endif
	int size = ACTIVE_STK_SZ(in_stk, TOP());
	
	if (size > t_stacksize) {
		task_error(E_STACK, this);
	}
	copy_stack(in_stk, size, t_basep);

	/* distance from old stack to new */
	register int distance = in_stk - t_basep;

	// save hardware state of this frame in "this" (child)
	t_framep = ta_fp - distance;

	/* now doctor the new frame */
#if defined(vax) || defined(PROC_3B)
	OLD_AP(t_framep) = int((int*)OLD_AP(ta_fp) - distance);
#endif
	OLD_FP(t_framep) = int((int*)OLD_FP(ta_fp) - distance);

#ifdef sparc
	//
	// Set up initial values for t_save_i6 and t_save_i7 into
	// the task's data area.   They must correspond to the %sp
	// value in t_framep.  Also, alter that extra frame's fp
	// to point into the newly copied stack.
	//
	t_save_i6 = (int*)OLD_FP(t_framep);
	t_save_i7 = (int*)OLD_PC(t_framep);

	int ofp = OLD_FP(ta_fp);
	OLD_FP(t_save_i6) = int((int*)OLD_FP(ofp) - distance);
#endif
	return distance;
}
inline
void
task::settrap()
{
	if (t_team->size) {	// Don't set trap for main task
		t_trap = *(STACK_LAST_WORD_P(
				STACK_BASE(t_team->stack, t_stacksize),
				t_team->size));
	}
}

inline
void
task::checktrap()
{
	if (t_team->size	// Don't test for main task
	    && t_trap != *(STACK_LAST_WORD_P(
				STACK_BASE(t_team->stack, t_stacksize),
				t_team->size))) {
		task_error(E_STACK, this);
	}

}

extern "C" {
	/* swap and sswap are assembly language functions */
extern task* swap(task*, task*, int, int);
extern task* sswap(task*, task*, task*, int, int);
}

//
// Note that the question of whether the translator actually decides to
// inline this function is irrelevant to whether it all works or not.
// Inilining it makes stack overflow a little bit less likely.
//
inline
void
task::restore(task* running, int is_new_child)
/*
 *	Call assembly function swap or sswap to do a task switch.
 *	Swap suspends "running" task by saving current hardware state (fp, etc)
 *	in running->t_framep, etc.,
 *	and makes "this" task run after suspension by returning from the frame
 *	denoted by "t_framep."
 *	
 *	swap does a normal return--must be the last thing called here.
 *	swap for a new child task will not return through restore,
 *	but will return from task::task().
 *
 *	sswap saves running's hw state, copies out the target stack,
 *	copies in to_run's stack from the save area before "swap"ing
 *	It is equivalent to two "copystack"s in the middle of "swap".
 *	sswap for a new child copies out the parent stack, but does not need
 *	to restore hw_state of child--it's already in place, and 
 *	WILL return through restore, unlike swap.
 */
{
	task* prevOnStack;
	// running might have been TERMINATED
	int is_terminated = 0;
	if ((running == 0) || (running->s_state == TERMINATED)) {
		is_terminated = 1;
	} else {
		running->checktrap();
	}

	if ((t_mode == SHARED) && this != (prevOnStack = t_team->got_stack)){
		t_team->got_stack = this;    // assuming sswap will get space.
		sswap(running, prevOnStack, this, is_new_child, is_terminated);
	}
	else  {	// DEDICATED or (SHARED && this already on target stack)
		swap(running, this, is_new_child, is_terminated);
	}
	//sswap and swap for old task return here.
	// This code is not needed for a new child, because a terminated
	// parent cannot create a new child.
	if (team_to_delete) {
		delete team_to_delete;
		team_to_delete = 0;
	}
	// WARNING!  No new code here.
}

task::task(char* name, modetype mode, int stacksize)
/*
	executed in the task creating a new task - thistask.
	1:	put thistask at head of scheduler queue,
	2:	create new task
	3:	transfer execution to new task
	derived::derived can never return - its return link is destroyed

	if thistask==0 then we are executing on main()'s stack and
	should turn it into the "main" task
*/
{
	register task* running;

	//
	// None of the stack copying and switching, etc, will work
	// on SPARC until the kernel flushes the registers in other
	// register-windows to the stack.  Since this (task::task) is
	// the *only* place where FP() is called, FP() is a relatively
	// safe place to do the flush.
	//
	register int* ta_fp = (int*)FP();	    // fp for task::task()
	register int* ta_ap = (int*)AP();	    // ap for task::task()
	register int* de_fp = (int*)OLD_FP(ta_fp);  // fp for ctor of class 
						    // derived from class task
	register int* de_ap = (int*)OLD_AP(ta_fp);
	register int* pa_fp = (int*)OLD_FP(de_fp);  // parent fp
						    // (caller of derived ctor)
	t_name = name;
	t_mode = (mode) ? mode : (modetype) DEFAULT_MODE;
	t_stacksize = (stacksize) ? stacksize : SIZE;
	t_alert = 0;
	s_state = RUNNING;
	t_next = txsk_chxin;
	txsk_chxin = this;
	th = this;	/* fudged return value -- "returned" from swap */

	switch ((int)thxstxsk) {
	case 0:
		/* initialize task system by creating "main" task */
		thxstxsk = (task*) 1;
		thxstxsk = new task("main");
		break;
	case 1:
		/*	create "main" task	*/
		usemainstack();		/* ensure that store is allocated */
		//set base pointer assuming a static task (Interrupt_alerter)
		//at this point stack has 6 frames for:
		//main, _main, <static ctor>, Int::Int, task::task, task::task
		//NOTE:  This sets basep to be the fp saved in _main's stack
		//frame (fp for main).  Depending on layout, may not include
		//main's save area.  This shouldn't matter.
		t_basep = (int*)OLD_FP((int*)OLD_FP((int*)OLD_FP(pa_fp)));
#ifdef sparc
		// On SPARC, it does matter if there will ever be SHARED tasks.
		t_basep = (int*)OLD_FP(t_basep);
#endif
		t_team = new team(this);	/* don't allocate stack */
		t_team->no_of_tasks = 2;   	/* never deallocate */
		return;
	}
	// thxstxsk is parent task
	/* return pointer to "child" */
	thxstxsk->th = this;
	thxstxsk->insert(0,this);

	switch (t_mode) {
	case DEDICATED:
		{
		t_team = new team(this,t_stacksize);
		t_basep = STACK_BASE(t_team->stack, t_stacksize);

		// initialize child's stack and the saved register(s)
		// (frame ptr, etc) in "this" (child).
		int distance = init_stack(ta_fp, pa_fp);

		if (thxstxsk->t_mode == SHARED)
			thxstxsk->t_size = ACTIVE_STK_SZ(t_basep, TOP());
		// save hardware state of this frame in "this" (child)
		t_framep = ta_fp - distance;
		t_ap = ta_ap - distance;
		settrap();

		// save all current and saved hw regs in New_task_regs,
		// so when child begins execution, it will have hw regs
		// as the derived constructor had them set.
		SAVE_CHILD_REGS(&New_task_regs);

		// thxstxsk->fudge_return(ta_fp);
		running = thxstxsk;	// running = parent
		thxstxsk = this;

#ifndef sparc
//
// The parent task:
//	This code fakes the return **for the parent task** so that the
//	current function (the task::task constructor) will return to
//	its grandparent, the caller of the derived constructor.  On 68k
//	and 386, this also arranges things so that the return (below)
//	from task::task will actually return to "fudge_sp", which
//	resets the stack pointer to look like it is about to return
//	from the frame of the skipped routine (ie., the derived
//	constructor).
// The child task:
//	restore calls swap or sswap, after which swap is running on the
//	stack that was copied by the init_stack call above.  So
//	although it's running inside swap, the active stack frame is a
//	copy of the frame for task::task.  So when the "is_new_child"
//	version of swap returns, it returns into the constructor for
//	the derived task class.  That constructor is the new child
//	task's main routine.
//
		running->fudge_return(ta_fp);
		restore(running, NEW_CHILD);	// no return for child; 
						// parent will return
		// Needed on some machines to reset sp on fudged stack
		FUDGE_SP(de_ap, de_fp);  // Can't access arguments on 3B now
					 // (except arg1, implicit "this")
#else
		//
		// On SPARC, all of the stack rearranging must occur on
		// the *inactive* stack, because of the kernel's implicit
		// flushing of register windows to the stack.
		//
		running->fudge_return(ta_fp);
		restore(running, NEW_CHILD);	// no return for child; 
						// parent will return
		// Needed on some machines to reset sp on fudged stack
		// FUDGE_SP(de_ap, de_fp);  // Can't access arguments on 3B now
					 // (except arg1, implicit "this")
#endif
		return;		// On 68k, return through fudge_sp()
		}
	case SHARED:
		thxstxsk->t_mode = SHARED; /* you cannot share on your own */
#ifdef sparc
		// Need extra frame on SPARC due to disjoint stack frame
		t_basep = (int*)OLD_FP( pa_fp );
#else
		t_basep = pa_fp;
#endif
		t_team = thxstxsk->t_team;
		t_team->no_of_tasks++;
		t_framep = ta_fp;
		t_ap = ta_ap;
		settrap();
		running = thxstxsk;	// running == parent
		thxstxsk = this;

		restore(running, NEW_CHILD);	// both parent & child will
						// return
		if (running == thxstxsk) {	// parent
			running->fudge_return(ta_fp);
			// Needed on some machines to reset sp on fudged stack
			FUDGE_SP(de_ap, de_fp);  // Can't access arguments
						 // on 3B now (except arg1,
					 	 // implicit "this")
			// On 68k, parent returns through fudge_sp()
		}
		return;
	default:
		task_error(E_TASKMODE, this);
	}
}

void
task::resume()
{
	task* running = thxstxsk;
	thxstxsk = this;
	restore(running);
}

void
task::cancel(int val)
/*
	TERMINATE and free stack space
*/
{
	if (this->s_state != TERMINATED) {
		sched::cancel(val);
		if (_hwm) t_size = curr_hwm();
		if (t_team && (t_team->no_of_tasks-- == 1)) {
			if (this != thxstxsk) {
				delete t_team;
			} else {	// don't delete current stack!
				// delete will be called from task::restore
				// immediately after task switch
				assert(team_to_delete == 0);
				team_to_delete = t_team;
			}
			t_team = 0;	// no further access to deleted team
		}
	}
	}

task::~task()
/*
	free stack space and remove task from task chain
*/
{
	if (s_state != TERMINATED) task_error(E_TASKDEL, this);
	if (this == txsk_chxin)
		txsk_chxin = t_next;
	else {
		register task* t;
		register task* tt;

		for (t=txsk_chxin; tt=t->t_next; t=tt)  
			if (tt == this) {
				t->t_next = t_next;
				break;
			}
	}

	if (this == thxstxsk) {
		delete (int*) thxstxsk;	/* fudge: free(_that) */
		thxstxsk = 0;
		schedule();
	}
}

void
task::resultis(int val)
{
	cancel(val);
	if (this == thxstxsk) schedule();
}

void
task::sleep(object* t)
{
	if (t) t->remember(this);
	if (s_state == RUNNING) remove();
	if (this == thxstxsk) schedule();
}

void
task::delay(long d)
{
	insert(d,this);
	if (thxstxsk == this) schedule();
}

long
task::preempt()
{
	if (s_state == RUNNING) {
		remove();
		return s_time - get_clock();
	}
	else {
		task_error(E_TASKPRE, this);
		return 0;
	}
}

char*
state_string(sched::statetype s)
{
	switch (s) {
	case sched::IDLE:		return "IDLE";
	case sched::TERMINATED:	return "TERMINATED";
	case sched::RUNNING:		return "RUNNING";
	default:		return 0;
	}
}

char*
mode_string(task::modetype m)
{
	switch(m) {
	case task::SHARED:	return "SHARED";
	case task::DEDICATED:	return "DEDICATED";
	default:		return 0;
	}
}


static void stk_dump ( int * addr, int nwords ) {

	for( int i = 0; i <= nwords-4; i += 4 ) {
	    printf( "0x%08x:  %8x  %8x    %8x  %8x\n",
		&addr[i], addr[i], addr[i+1], addr[i+2], addr[i+3] );
	}
	// DB(( "nwords %d - i %d == %d\n", nwords, i, nwords - i ));
	switch( nwords - i ) {
	 case 0:
		printf( "Done.\n" );
		break;
	 case 1:
		printf( "0x%08x:  %8x  XXXXXXXX    XXXXXXXX  XXXXXXXX\n",
		    &addr[i], addr[i] );
		break;
	 case 2:
		printf( "0x%08x:  %8x  %8x    XXXXXXXX  XXXXXXXX\n",
		    &addr[i], addr[i], addr[i+1] );
		break;
	 case 3:
		printf( "0x%08x:  %8x  %8x    %8x  XXXXXXXX\n",
		    &addr[i], addr[i], addr[i+1], addr[i+2] );
		break;
	 default:
		printf( "\nAACK!\n" );
	}
}


void
task::print(int n, int baseClass)
/*
	"n" values:	CHAIN, VERBOSE, STACK
			DUMP_STACK, NO_SEPARATOR
*/
{
	int * real_fp = FP( );

	// We need some visual separation here...
	if( (n&NO_SEPARATOR) == 0 ) {
		printf(
"========================================================================\n"
		);
	}

	if (!baseClass) {
		printf("task\n");
	}

	char* ss = state_string(s_state);
	char* ns = (t_name) ? t_name : "";

	printf("task %s ",ns);
	if (this == thxstxsk)
		printf("(is thistask, %s): ", ss);
	else if (ss)
		printf("(%s) ",ss);
	else
		printf("(state==%d CORRUPTED) ",s_state);
	printf("\tthis = %x:\n", this);

	if (n&STACK) {
#ifdef sparc
		printf("\ttask::print FP (sparc %%sp) == 0x%x\n", real_fp );
#else
		printf("\ttask::print FP == 0x%x\n", real_fp );
#endif
	}

	if (n&VERBOSE) {
		char* ms = mode_string(t_mode);
		if (ms == 0) ms = "CORRUPTED";
		printf("\tmode=%s t_alert=%x t_next=%x",
			ms, t_alert, t_next);
		printf((s_state==TERMINATED) ? " result=%d\n" : " s_time=%d\n", s_time);
	}

	if (n&STACK) {
		printf("\tstack: ");
		if (s_state == TERMINATED) {
			printf("deleted.  ");
			if (_hwm) {
				printf("hwm size=%d, ", t_size);
				printf("hwm address=%x",STACK_LAST_WORD_P(t_basep,t_size));
			}
			printf("\n");
		}
		else {
			register int* b = t_basep;
			printf("\tsizes:\t");
			register int sz;
			if (this==thxstxsk) { // figure out real current size
				sz = ACTIVE_STK_SZ(b,TOP());
			} else {	// approximate at last switch
				sz = t_mode==DEDICATED ?
					ACTIVE_STK_SZ(b,t_framep) : t_size;
			}
			printf("max=%d, current=%d",t_stacksize, sz);
			if (_hwm) printf(", hwm=%d",curr_hwm());
			printf("\n\t\taddresses:\t");
			printf("t_basep=%x, t_framep=%x\n",b,t_framep);
			printf("\t\t\t\tmax=%x",
				STACK_LAST_WORD_P(b,t_stacksize));
			printf(", current=%x", STACK_LAST_WORD_P(b,sz));
			if (_hwm) printf(", hwm=%x",
				STACK_LAST_WORD_P(b, curr_hwm()));
			printf("\n");
#ifdef sparc
			printf(" FRAMEP=0x%x, t_save_i6=0x%x, t_save_i7=0x%x\n",
				t_framep, t_save_i6, t_save_i7 );
#endif
		}
	}

	if (n&DUMP_STACK) {
		if( t_framep == 0 ) {
			printf( "\tNO STACK.\n" );
		} else if( t_framep ) {
			int * task_fp = t_framep;

			// Can't easily tell if real_fp is on the stack that
			// corresponds to "this", so we use it only if it's
			// "close enough".
			if( real_fp <= t_framep  &&  t_framep < real_fp + SIZE ) {
			    task_fp= real_fp;
			}

			int nwords = t_basep - task_fp ;
			if( nwords > 256 ) nwords = 256;
			if( nwords <= 0 ) {
				printf( "\tBAD STACK OR BASE PTR.\n" );
			} else {
				stk_dump( task_fp, nwords );
			}
        	}    
	}

	if (n&CHAIN) {
		sched::print(n, 1);	// call sched::print here to keep
					// output for same object together
		// Start at beginning of task chain, and print all tasks
		task *tp = get_task_chain();
		if (tp == this) {
			tp = tp->t_next;	// just printed, skip it
		} else {
			printf("\nChain of all tasks:\n");
		}
		for (; tp; tp = tp->t_next) {
			printf("Next task on chain of all tasks is:\n");
			tp->print( (n & ~CHAIN) | NO_SEPARATOR );
		}
	} else {
		sched::print(n, 1);
	}
}

int
task::curr_hwm()
{
	int* b = t_basep;
	int i;
	for (i=t_team->size; 0<i && *(STACK_LAST_WORD_P(b,i))==UNTOUCHED; i--) ;
	return i;
}

void
task::wait(object* ob)
{
	if (ob == (object*)this) task_error(E_WAIT, this);
	t_alert = ob;
	while (ob->pending())
		sleep(ob);
}

int
task::waitlist(object* a ...)
{
	return waitvec(&a);
}

int
task::waitvec(object** v)
/*
	first determine if it is necessary to sleep(),
	return hint: who caused return
*/
{
	int i;
	int j;
	register object* ob;

	for(;;) {
		for (i = 0; ob = v[i]; i++) {
			if (!ob->pending()) goto ex;
			ob->remember(this);
		}
		if (i==1 && v[0]==(object*)this) task_error(E_WAIT, this);
		sleep();
	}
ex:
	t_alert = ob;
	for (j = 0; ob = v[j]; j++)
		ob->forget(this);
	return i;
} 

Interrupt_alerter	interrupt_alerter;
« March 2024 »
Su Mo Tu We Th Fr Sa
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31
 

Powered by Plone CMS, the Open Source Content Management System

This site conforms to the following standards: