/*
	Copyright 12/02/93 Sun Microsystems, Inc. All Rights Reserved
*/
/*  TASK.H - C++ task library.

    Copyright (C) 1990, 1993 TauMetric Corporation, All Rights Reserved

    @(#)task.h	1.5  12/02/93 13:04:39

    This is a re-implementation of the AT&T task library.  Only
    the interface is taken from the AT&T library.  If your code
    depends on any private members or implementation details of the
    AT&T library it will not work.  Code which uses only the public
    interface should work without alterations.

*/

#ifndef _TASK_H_
#define _TASK_H_

#include <signal.h>

#define SIZE	8192	// default size of task stack - 2048 longwords
#ifdef _SHARED_ONLY
#  define DEFAULT_MODE task::SHARED	// default task mode
#else
#  define DEFAULT_MODE task::DEDICATED	// default task mode
#endif

class object;
class qhead;
class qtail;
class sched;
class task;
class timer;
class team;

typedef int (*PFIO)(int,object*);
typedef void (*PFV)();

// print flags, used as arguments to class print functions
#define CHAIN		1
#define VERBOSE		2
#define STACK		4

/*
 * The class "object" is used as a base type by all of the classes
 * defined in the task library.  The name is unfortunate as it is
 * commonly used in a variety of library packages.  Evidently, all
 * of these packages assume that they are the only ones you will
 * will ever want to use.
 */

// for building chains of task pointers
class olink {
friend object;
    olink* l_next;	// next link
    task* l_task;	// this task
    olink(task*, olink*);
};

inline olink::olink(task* t, olink* l) { l_task=t; l_next=l; }


class object {
//friend sched;
public:
    /*
     * Public constants
     */
    enum objtype { OBJECT, TIMER, TASK, QHEAD, QTAIL, INTHANDLER };
	// This enumeration identifiers the type of object, and should have
	// an entry added for each new class derived from object

    /*
     * Public data members
     */
    object*	o_next;		// points to the next in a list of objects.
    static	PFIO error_fct;	// holds a user-defined error function

    /*
     * Virtual Functions
     */
    virtual objtype o_type();	// return the current object type 
    virtual int	pending();	// TRUE if needs to be waited on
    virtual void print(int, int=0); // print current object status on stdout

    /*
     * Common functionality
     */
    void alert();	// Make remembered tasks eligible for execution
    void forget(task*);	// Forget a previously remembered task
    void remember(task*); // Remember a task for alert

    /*
     * Static member functions
     */
    static int task_error(int, object*); // main error function for all classes
    int	task_error(int);	// obsolete, use task_error(int, object*);
    static task* this_task();	// return the currently running task

    /*
     * Constructors and destructors
     */
    object();
    virtual ~object();

protected:
    olink* memory;		// The root of the remember chain
    static task* running;	// The running task
};

inline int object::pending() { return 1; }
inline int object::task_error(int err) { return object::task_error(err, this); }
inline task* object::this_task() { return running; }

/*
 * The "sched" class contains the common behavior for scheduable
 * objects, both timers and tasks.  Its only constructor is protected
 * so it can only be derived from
 */

class sched: public object {
friend object;
friend task;
friend timer;
public:
    /*
     * Public Constants
     */
    enum statetype { IDLE=1, RUNNING=2, TERMINATED=4 }; // task or timer state

    /*
     * Public Data Members
     */
    static task* clock_task;	// Call this task when the clock advances
    static PFV exit_fct;	// Call this function on exit from program

    /*
     * Virtual Functions
     */
    // objtype o_type(); 	Inherited from object
    virtual int	pending();		// True if not terminated
    virtual void print(int, int =0);	// print current status on stdout
    virtual void setwho(object*);	// Record object which sent the alert

    /*
     * Common behavior
     */
    void cancel(int);		// Terminate the task and set result
    int	dont_wait();		// No longer wait for interrupt
    sched* get_priority_sched(); // Return priority-scheduled task
    int	keep_waiting();		// Even if no more tasks, wait for interrupt
    statetype rdstate();	// Return the current state of the object
    long rdtime();		// Return the clock time for next execution
    int	result();		// The result of the task

    /*
     * Static Members
     */
    static void	setclock(long);	// Set the initial clock value
    static long	get_clock();	// Return the current clock value
    static sched* get_run_chain(); // Return start of list of eligible objects
    static int get_exit_status(); // Find what program exit status will be
    static void set_exit_status(int i); // Set the program exit status

protected:
    /*
     * Constructors and destructors
     */
    sched();

private:
    /*
     * private functions
     */
    void insert(int, object*);	// add self to run chain with delay, obj alerted
    void insert();		// add self to front of run chain
    void remove();		// remove self from run chain and make IDLE
    void schedule();		// resume task at front of runchain
    virtual void resume() = 0;	// resume self, not implemented here

    /*
     * static private variables
     */
    static long s_clock;	// current clock value
    static sched* s_runchain;	// list of runnable objects
    static int s_exit_status;	// program exit status
    static sched* s_priority;	// priority-scheduled task
    static int s_wait_count;	// net keep_waiting/dont_wait calls

    /*
     * ordinary private variables
     */
    long s_time;		// if RUNNING, time at which to run
				// if TERMINATED, result of task
    statetype s_state;		// scheduler state
};

inline int sched::dont_wait() { return s_wait_count--; }
inline int sched::pending() { return s_state != TERMINATED; }
inline int sched::keep_waiting() { return s_wait_count++; }
inline sched::statetype sched::rdstate() { return s_state; }
inline long sched::rdtime() { return s_time; }
inline sched* sched::get_priority_sched() { return s_priority; }
inline long sched::get_clock() { return s_clock; }
inline sched* sched::get_run_chain() { return s_runchain; }
inline int sched::get_exit_status() { return s_exit_status; }
inline void sched::set_exit_status(int i) { s_exit_status = i; }

/*
 * The task class is responsible for all task bookkeeping.  User
 * tasks should be derived (one level only) from class task.  The
 * constructor will become the body of the task
 */

extern int _hwm;	// If true, keep stack use statistics for each task

class task: public sched {
friend class Start_Main_Task;
public:
    /*
     * Public Constants
     */
    enum modetype { DEDICATED=1, // each task has its own stack
		   // SHARED=2	// shared common stack area, with copying
		    SHARED=1	// shared disabled for SPARC architecture

		  };

    /*
     * Public data members
     */
    task* t_next;	// Links all tasks in a chain
    char* t_name;	// Task name (set by constructor)

    /*
     * Virtual Functions
     */
    virtual objtype o_type();		// Return object type
    virtual void print(int, int =0);	// Print object current status on stdout
    virtual void setwho(object*);	// Record object who alerted this one.

    /*
     * Class behavior
     */
    void cancel(int);	// Terminate the task and set result
    void delay(int);	// Resume execution after a time delay
    int	preempt();	// Suspend a running task, making it idle
    void resultis(int);	// Terminate the task and set the result status
    void sleep(object* =0); // Suspend a task, optionally waiting on an object
    void wait(object*);	// Suspend until object is not pending
    int	waitlist(object* ...); // Wait for one out of a list of objects
    int	waitvec(object**); // Same as waitlist except array of objects
    object* who_alerted_me(); // Return pointer to object who alerted this task

    /*
     * Static member functions
     */
    static task* get_task_chain(); // Get the list of all tasks

    /*
     * Constructors and destructors
     */
    ~task();

protected:
	// set task name, mode and stacksize,  also does the fork
    task(char* = 0, modetype = DEFAULT_MODE, int = SIZE);

private:
    /*
     * static private variables
     */
    static task *t_task_chain;	// list of all tasks

    /*
     * ordinary private variables
     */
     object *t_alert;		// object which alerted

     /*
      * implementation variables and functions
      * DO NOT CHANGE THE ORDER OF THESE VARIABLES OR INSERT OTHERS
      */
    char *t_stackb;		// stack base for this task
    char *t_stacke;		// stack end for this task
    char *t_stackp;		// stack pointer for this task
    char *t_framep;		// frame pointer for this task
    char *t_retaddr;		// return address for this task
    char *t_savearea;		// shared stack save area
    char *t_lwm;		// "low-water mark" of shared task stack
    int   t_savesize;		// saved stack size
    team *t_team;		// task team data
    short t_mode;		// shared or dedicated
    short t_continue;		// for continue_constructor

    task(short);			// special constructor for "main task"
    void resume();			// resume self
};

inline object* task::who_alerted_me() { return t_alert; }
inline task* task::get_task_chain() { return t_task_chain; }

/*
 * The following ensures proper initialization of "main" as a task
 */
static class Start_Main_Task {
    static task *Main_Task;
public:
    Start_Main_Task();
} start_main_task;


/*
 * Class timer implements a simple delay and terminates when that
 * is gone.  It is useful for timeouts and similar functions
 */

class timer: public sched {
public:
    /*
     * Virtual functions
     */
    virtual objtype o_type();		// Return type of current object
    virtual void print(int, int =0);	// Print current object status on stdout
    virtual void setwho(object*);	// Record object who alerted this one

    /*
     * Object behavior
     */
    void reset(int);	// Reset and restart the timer

    /*
     * constructors and destructors
     */
    timer(int);		// Delay for the length specified
    ~timer();

private:
    void resume();		// resume self
};

/*
 * Class Interrupt_handler provides an interface to the signal
 * mechanism defined by UNIX or ANSI C.
 */

static SIG_FUNC_TYP signal_handler; // local function in inthand.cc

class Interrupt_handler : public object {
public:
    /*
     * Virtual Functions
     */

    virtual objtype o_type();	// Return object type of current object
    virtual int	pending();	// False once after each interrupt
    // virtual void print(int, int =0);	inherited from object

    /*
     * Constructors and destructors
     */
    Interrupt_handler(int);	// Create handler for a signal
    ~Interrupt_handler();

private:
    virtual void interrupt();	// the interrupt handler function

    int signo;			// signal number
    int gotint;			// got an interrupt but not alert not done
    Interrupt_handler *prev;	// previous handler for this signal
    Interrupt_handler *next;	// Makes a chain of handlers
    SIG_FUNC_TYP* oldsig;	// Signal handler before this one

    friend SIG_FUNC_TYP signal_handler;
    friend class Interrupt_alerter;
};

/*
 * Queues are made up of a qhead and qtail.  These do the bookkeeping
 * necessary for inserting and deleting objects from the queue
 */

// Modes which determine actions when the queue is full or empty
enum qmodetype { EMODE,	// error on empty get or full put
		 WMODE,	// wait (block) on empty get or full put
		 ZMODE	// return 0 on empty get or full put
		};


/*
 * Common queue data for a qhead/qtail pair
 */
class oqueue {
friend qhead;
friend qtail;

    oqueue(int);
    ~oqueue();
    void print(int);

    int	q_max;		// max objects in the queue
    int	q_count;	// how many now in queue
    object *q_ptr;	// points to tail of circular queue
    qhead *q_head;	// associated qhead
    qtail *q_tail;	// associated qtail
};


class qhead: public object {
friend qtail;
public:
    /*
     * Virtual Functions
     */
    virtual objtype o_type();	// Return the type of current object
    virtual int	pending();	// TRUE if queue is empty
    virtual void print(int, int=0); // Print current status object on stdout

    /*
     * Class operations
     */
    qhead* cut();	// split the queue, returning the right side
    object* get();	// get the head of the queue
    int	putback(object*); // Return an object to the head of the queue
    int	rdcount();	// Return the number of objects on the queue
    int	rdmax();	// Return the max number of objects on the queue
    qmodetype rdmode();	// Return the queue mode
    void setmode(qmodetype); // Set the current queue mode
    void setmax(int);	// Set the max objects allowed
    void splice(qtail*); // Reverse the operation of cut.
    qtail* tail();	// Return the tail attached to this head

    /*
     * Constructors and destructor
     */
    qhead(qmodetype=WMODE, int=10000); // qhead with given mode and max size
    ~qhead();

private:
    qmodetype mode;		// queue mode
    oqueue *q;			// common queue data
};

inline int qhead::rdcount() { return q->q_count; }
inline int qhead::rdmax() { return q->q_max; }
inline qmodetype qhead::rdmode() { return mode; }
inline void qhead::setmax(int m) { q->q_max = m; }


class qtail: public object {
friend qhead;
public:
    /*
     * Virtual Functions
     */
    virtual objtype o_type();	// Return type of current object
    virtual int	pending();	// TRUE if queue is full
    virtual void print(int, int = 0); // Print current object status on stdout

    /*
     * Class operations
     */
    qtail* cut();	// Cut an existing queue
    qhead* head();	// Return the head attached to this tail
    int	put(object*);	// Add a new object to tail of queue
    int	rdmax();	// Return the max objects allowed
    qmodetype rdmode();	// Return the mode for the queue
    int	rdspace();	// Return the space left in the queue
    void setmode(qmodetype); // Set the mode of the queue
    void setmax(int);	// Set the max size of the queue
    void splice(qhead*); // Undo the effects of a cut

    /*
     * Constructor and destructor
     */
    qtail(qmodetype=WMODE, int=10000); // qtail with given mode and max size
    ~qtail();

private:
    qmodetype mode;		// queue mode
    oqueue *q;			// common queue data
};

inline int qtail::rdmax() { return q->q_max; }
inline qmodetype qtail::rdmode() { return mode; }
inline int qtail::rdspace() { return q->q_max - q->q_count; }
inline void qtail::setmax(int m) { q->q_max = m; }


/* A histogram is used to accumulate statistics in a simulation.
 * It has "nbin" bins covering the range [l:r].  If given a value
 * outside that range it expands the range (but not nbin) to
 * fit it.  The existing counts will be redistributed over the
 * new bins.  nbin*binsize == (r-l)
 */

struct histogram {
    /*
     * Data members
     */
    int	l, r;		// Left and right boundaries of the histogram
    int	binsize;	// Size of each bin
    int	nbin;		// Number of bins
    int* h;		// Pointer to storage
    long sum;		// Sum of all entries
    long sqsum;		// Sum of squares of all entries

    /*
     * Functions
     */
    void add(int);	// Add a count to the histogram
    void print();	// Print the current histogram

    /*
     * Constructor
     */
    histogram(int=16, int=0, int=16); // specifying nbins, l and r
};

/*
 * Pseudo-random number classes
 */

// Basic uniform random numbers
class randint {
    long state;
public:
    int	draw();		// Uniform ints over the range [0 : MAXINT]
    float fdraw();	// Uniform floats over the range [0.0 : 1.0]
    double ddraw();	// Uniform doubles over the range [0.0 : 1.0]
    void seed(long);	// Set the seed and reinitialize the stream

    randint(long = 0);	// specified seed.
};


// Uniform distribution over an interval
class urand: public randint {
public:
    int	low, high;	// Limits of the distribution

    int	draw();		// Produce an integer in the range [low : high]

    urand(int, int);	// specifying low and high
};


// Exponential distribution of integers
class erand : public randint {
public:
    int	mean;	// mean for the distribution

    int	draw();	// Produce an int in an exponential distribution about mean

    erand(int);	// specified mean
};

const int E_ERROR = 0;
const int E_OLINK = 1;
const int E_ONEXT = 2;
const int E_GETEMPTY = 3;
const int E_PUTOBJ = 4;
const int E_PUTFULL = 5;
const int E_BACKOBJ = 6;
const int E_BACKFULL = 7;
const int E_SETCLOCK = 8;
const int E_CLOCKIDLE = 9;
const int E_RESTERM = 10;
const int E_RESRUN = 11;
const int E_NEGTIME = 12;
const int E_RESOBJ = 13;
const int E_HISTO = 14;
const int E_STACK = 15;
const int E_STORE = 16;
const int E_TASKMODE = 17;
const int E_TASKDEL = 18;
const int E_TASKPRE = 19;
const int E_TIMERDEL = 20;
const int E_SCHTIME = 21;
const int E_SCHOBJ = 22;
const int E_QDEL = 23;
const int E_RESULT = 24;
const int E_WAIT = 25;
const int E_FUNCS = 26;
const int E_FRAMES = 27;
const int E_REGMASK = 28;
const int E_FUDGE_SIZE = 29;
const int E_NO_HNDLR = 30;
const int E_BADSIG = 31;
const int E_LOSTHNDLR = 32;
const int MAXERR = E_LOSTHNDLR;

/* The following definitions are included for compatibility with
 * earlier versions of the package.  They should not be used in new code.
 */

#define thistask (object::this_task())

//#define clock (sched::get_clock())	 // conflicts with time.h

inline void setclock(long i) { sched::setclock(i); }

#define run_chain (sched::get_run_chain())

#define task_chain (task::get_task_chain())

/* Not sure why the following is in the public header, but I
 * assume that some programs may make direct reference to it.
 */

// Special task to alert Interrupt objects

class Interrupt_alerter : public task {
public:
	Interrupt_alerter();
	~Interrupt_alerter();
	objtype o_type();
};

extern Interrupt_alerter interrupt_alerter;

#endif
