/*ident	"@(#)cls4:lib/task/task/sched.c	1.2" */
/*******************************************************************************
 
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.

*******************************************************************************/
#include <task.h>
/* Most environments have wait(2) declared in osfcn.h, but BSD has it
 * in sys/wait.h. */
#include <osfcn.h>
#ifdef BSD
#include <sys/wait.h>
#endif

void
sched::setclock(long t)
{
	if (clxck) object::task_error(E_SETCLOCK, (object*)0);
	clxck = t;
}

void
sched::cancel(int res)
{
	if (s_state==RUNNING) remove();
	s_state = TERMINATED;
	s_time = res;
	alert();
}

int
sched::result()
/* wait for termination and retrieve result */
{
	if (this == (sched*)this_task()) task_error(E_RESULT, this);
	while (s_state != TERMINATED) {
		this_task()->sleep(this);
	}

	return (int) s_time;
}

void
sched::schedule()
/* schedule either clock_task or front of runchain */
{
	register sched* p;
	register long tt;

keep_waiting:
	if (p = priority_sched) {
		priority_sched = 0;  // no chain here
		p->remove();  // in case it's also on the runchain
		p->s_state = RUNNING;  // since remove sets it IDLE
	}
	else if (p = runchain) {
		runchain = (sched*) p->o_next;
		p->o_next = 0;
	}
	else {
		if (keep_waiting_count) {
			::pause();
			goto keep_waiting;
		}
		if (exit_fct) (*exit_fct)();
		for (p = task::txsk_chxin; p; p = ((task*)p)->t_next) {
			if (p->s_state == RUNNING) p->remove();
			p->s_state = TERMINATED;
			p->s_time = 0;
		}
		exit(exit_status);
	}

	tt = p->s_time;
	if (tt != clxck) {
		if (tt < clxck) task_error(E_SCHTIME, this);
		clxck = tt;
		if (clock_task) {
			if (clock_task->s_state != IDLE)
				task_error(E_CLOCKIDLE, this);
			/* clock_task preferred -- put p back onto runchain */
			p->o_next = (object*) runchain;
			runchain = p;
			p = (sched*) clock_task;
		}
	}
	if (p != this)
		p->resume();
} /* schedule */

void
sched::insert(long d, object* who)
/*
	schedule THIS to run in ``d'' time units
	inserted by who
*/
{
	register sched * p;
	register sched * pp;
	register long tt = s_time = clxck + d;

	switch (s_state) {
	case TERMINATED:
		task_error(E_RESTERM, this);
		break;
	case IDLE:
		break;
	case RUNNING:
		if (this != (class sched *)this_task()) task_error(E_RESRUN, this);
	}

	if (d<0) task_error(E_NEGTIME, this);	

	if (o_next) task_error(E_RESOBJ, this);

	s_state = RUNNING;
	setwho(who);

	/* runchain ordered by s_time */
	if (p = runchain) {
		if (tt < p->s_time) {
			o_next = (object*) runchain;
			runchain = this;
		}
		else {
			while (pp = (sched *) p->o_next) {
				if (tt < pp->s_time) {
					o_next = pp;
					p->o_next = this;
					return;
				}
				else p = pp;
			}
			p->o_next = this;
		}
	}
	else 
		runchain = this;
}

void
sched::remove()
/* remove from runchain and make IDLE */
{
	register class sched * p;
	register class sched * pp;

	if (p = runchain)
		if (p == this)
			runchain = (sched*) o_next;
		else
			for (; pp = (sched*) p->o_next; p=pp)
				if (pp == this) {
					p->o_next = pp->o_next;
					break;
				}
	s_state = IDLE;
	o_next = 0;
}

void
sched::print(int n, int baseClass)
{
	if (!baseClass)
		printf("naked sched (object not part of class derived from sched)");
	if (n&VERBOSE) {
		printf("\tsched:  this=%x\n", this);
	}
	if (n&CHAIN) {
		object::print(n, 1);	// call object::print here to keep
					// output for same object together
		// If this is a task, all task objects are already being
		// printed under CHAIN.  Therefore, don't call print
		// recursively for tasks.  If current task is thistask,
		// print entire run_chain with 0 arg, to keep it short.
		if (o_type() == TASK) {
			if (this == thxstxsk) {
				sched *sp = get_run_chain();
				if (sp == 0) {
					printf("run chain is empty\n");
				} else {
					printf("run chain is:\n");
					for (; sp; sp = (sched*)sp->o_next) {
						sp->print(0);
					}
					printf("end of run chain.\n");
				}
			}
		} else if (o_next)  {
			printf("Next sched object on run chain is:\n");
			((sched*) o_next)->print(n);
		} 
	} else {
		object::print(n, 1);
	}

	if (!baseClass)
		task_error(E_SCHOBJ, this);  // only derived class instances allowed

}

/* sched::resume() is a virtual function.  Because sched is not intended
 * to be used directly, but only as a base class, this should never be called.
 * Must define resume for each class derived from sched.
 */
void
sched::resume()
{
	task_error(E_SCHOBJ, this);
}

/* sched::setwho() is a virtual function.  Because sched is not intended
 * to be used directly, but only as a base class, this should never be called.
 * Must define setwho for each class derived from sched.
 */
void
sched::setwho(object*)
{
	task_error(E_SCHOBJ, this);
}
