home *** CD-ROM | disk | FTP | other *** search
- /**********************************************************************
- *
- * NAME: task.h
- *
- * DESCRIPTION: header for multi-tasking scheduler
- *
- * copyright (c) 1991 J. Alan Eldridge
- *
- * Warning: because of a bug in the Borland C++ clock() function,
- * timeouts may not work correctly when crossing midnight boundaries.
- *
- * M O D I F I C A T I O N H I S T O R Y
- *
- * when who what
- * -------------------------------------------------------------------
- * 03/12/91 J. Alan Eldridge created
- *
- * Mar-May 91 J. Alan Eldridge many, many revisions
- *
- * 10/23/91 JAE added code to force task stack size
- * up to reasonable minimum value
- *
- * 10/26/91 JAE added support for DJ Delorie's port
- * port of GNU C++ v. 1.39
- *
- * 11/09/91 JAE rewrote to use SysQP class for
- * Sema, Task queues
- *
- * tasks now can have priorities...
- * (be careful of starvation!)
- *
- * 11/11/91 JAE added stack basher checking in class
- * Task, called by scheduler
- *
- * 11/13/91 JAE removed obsolete queue types
- *
- * Semas can now be prioritized
- * by Task priority (so the highest
- * priority Task gets its wait() first)
- * ... MBoxes also have this feature
- *
- * 11/16/91 jae rewrote pipes to use generic.h <arrgh!>
- * (I just can't maintain two sets of source,
- * one with templates and one without... when
- * everybody is using C++ 3.0, I'll replace
- * the generics with templates.)
- *
- * 11/21/91 jae added class ChildTask (so you can create
- * a task from within another task, and
- * then wait for it to finish before
- * continuing)
- *
- * 11/30/91 jae added fDelete flag to Task, so you can
- * dynamically create a new Task on the heap,
- * and then proceed, knowing that the storage
- * will eventually be freed when the task
- * is killed or commits sucicide
- *
- *********************************************************************/
-
- #ifndef __TASK_H
- #define __TASK_H
-
- #include <generic.h>
-
- #define SUSPEND_ON_WAIT 0 // if 1, suspend on Sema.wait() even if
- // resource is available (if 0, don't suspend)
-
- #if !defined(__TURBOC__) && !defined(__GNUG__)
- #error Turbo C++/Borland C++ or GNU C++ required!
- #endif
-
- class Task; // forward decl for typedefs
-
- #include "sysqvfnc.h"
-
- #define GetNxtTask(q) ((Task*)((q).Get()))
-
- // Task++ version number
-
- #define TASKPP_VERSION "3.10"
-
- //------------------------------------------------------------
- // ********** SCHEDULER **********
- //
- // the scheduler has only 1 public entry point:
- // scheduler(arg) -- starts up the tasks (arg != 0)
- // -- shuts down all tasks (arg == 0)
- //------------------------------------------------------------
-
- extern int scheduler(int tasking = 1);
-
- //------------------------------------------------------------
- // a ptr to the currently executing task is kept in CurrTask
- //------------------------------------------------------------
-
- class Task; // forward reference
-
- extern Task *CurrTask;
-
- //------------------------------------------------------------
- // everything that takes a wait time argument can also give
- // one of these values
- //------------------------------------------------------------
-
- #define NoWait 0L
- #define WaitForever -1L
-
- //------------------------------------------------------------
- // ********** CLASS SEMAPHORE **********
- //
- // semaphores are used to control access to resources,
- // such as the keyboard, a printer, etc.
- //------------------------------------------------------------
-
- class SemaBase {
- private:
- int s_value; // resource count
- protected:
- SysQP *s_waiting; // queue of tasks waiting
- public:
- SemaBase(int val = 0): s_value(val) { }
- virtual ~SemaBase()
- { delete s_waiting; }
- // check value
- int avail()
- { return s_value > 0; }
- operator int() // as in: if (sema) ...
- { return avail(); }
- // wait on sema
- int wait(clock_t msec = WaitForever);
- void operator--()
- { wait(); }
- #ifdef CPP_2_1
- void operator--(int i)
- { wait(); }
- #endif
- // signal sema
- void signal(int susp = 1);
- void operator++()
- { signal(); }
- #ifdef CPP_2_1
- void operator++(int i)
- { signal(); }
- #endif
- };
-
- class Sema: public SemaBase {
- public:
- Sema(int val=0): SemaBase(val)
- { s_waiting = new SysQP; }
- };
-
- class SemaPri: public SemaBase {
- public:
- SemaPri(int val=0): SemaBase(val)
- { s_waiting = new SysPriQP; }
- };
-
- //------------------------------------------------------------
- // ********** CLASS TASK **********
- //
- // class Task implements the basic schedulable entity...
- //------------------------------------------------------------
-
- class Task: public Qable {
- private:
- friend class SemaBase;
- friend int scheduler(int tasking);
- friend void SchWakeup();
-
- static const uchar stkval; // value to fill stack with
-
- char *tskname; // name of task
-
- // flags for task state
- uint fInited:1, // initialized
- fReady:1, // ready to run
- fTimed:1, // blocked w/timeout
- fZombie:1, // dead but not buried
- fDelete:1; // should delete when dead
-
- jmp_buf tskEnv; // environment for context switching
- int stklen; // length of task's stack
- uchar *stack; // this task's stack space
- clock_t wakeUp; // when to wake up if asleep
- int tskpri; // task priority
-
- // get state of flags
- int isInited()
- { return fInited; }
- int isReady()
- { return fReady; }
- int isTimed()
- { return fTimed; }
- int isZombie()
- { return fZombie; }
- int shouldDelete()
- { return fDelete; }
-
- // clear fTimed flag and return previous value
- int timeOut();
- // set up stack and begin execution
- void init();
- // return to place of last suspend()
- void resume(int code = 1)
- { longjmp(tskEnv, code); }
- // possibly wake up a timed task
- int maybeWake();
- // if blocked, task will wait until another unblocks it
- int block(clock_t msec = WaitForever);
- // make task ready to execute again
- void unblock();
- // check if stack bashed
- int stackok();
- public:
- // enum for minimum allowed stack size
- #if defined(__TURBOC__)
- #if defined(_Windows)
- enum { StackMin = 0x2000 };
- #else
- enum { StackMin = 0x1000 };
- #endif
- #elif defined(__GNUG__)
- enum { StackMin = 0x2000 };
- #endif
- // constructor, destructor
- Task(char *tname, int stk = StackMin);
- virtual ~Task()
- { delete stack; }
- // get name of task
- char *name()
- { return tskname; }
- // set the delete flag
- void setDelete()
- { fDelete = 1; }
- // shut down one task
- virtual void suicide();
- // the main routine for each task
- virtual void TaskMain() = 0;
- // voluntarily give up control
- int suspend();
- // wait a specified amount of time
- void sleep(clock_t msec)
- { block(msec); }
- // get, set priority
- int priority()
- { return tskpri; }
- int priority(int newpri)
- { int oldpri = tskpri; tskpri = newpri; return oldpri; }
- // compare tasks for priority
- int operator<(Qable &t)
- { return tskpri < ((Task*)(&t))->tskpri; }
- };
-
- // these functions are for calls from non-member functions ...
- // they are strictly for convenience in avoiding the CurrTask-> syntax
-
- inline char *TaskName() { return CurrTask->name(); }
- inline void TaskSuicide() { CurrTask->suicide(); }
- inline int TaskSuspend() { return CurrTask->suspend(); }
- inline void TaskSleep(clock_t msec) { CurrTask->sleep(msec); }
- inline int TaskPriority() { return CurrTask->priority(); }
- inline int TaskPriority(int newpri){ return CurrTask->priority(newpri); }
-
- // kill an arbitrary task, including the current one
- inline void TaskKill(Task *t) { t->suicide(); }
-
- // report a fatal error and die with a call to exit(1)
- // NOTE: the user is free to replace this routine if desired
- void TaskFatal(char *fmt, ...);
-
- //------------------------------------------------------------
- // ********** CLASS CHILDTASK **********
- //
- // this is a class that implements a child task: the parent waits
- // for the child to die before continuing...
- //
- // e.g.:
- //
- // ChildTaskDerived *pcht; // ptr to child task
- //
- // pcht = new ChildTaskDerived("MyChild"); // create new task
- // pcht->wait(); // wait for new task to die
- // delete pcht; // destroy new task (now that it's dead)
- //------------------------------------------------------------
-
- class ChildTask: public Task, public Sema {
- public:
- ChildTask(char *tname, int stk=StackMin): Task(tname,stk)
- { }
- virtual void suicide()
- { signal(0); Task::suicide(); }
- };
-
- //------------------------------------------------------------
- // ********** CLASS MBOX **********
- //
- // class MBox implements a mailbox: this is an intertask
- // communication device that implements a rendezvous type
- // of interaction; that is, a send must block until a receiver
- // has picked up the message. A sender or receiver may specify
- // a timeout period.
- //
- // Note that class MBoxPri (a mailbox with priority semaphores)
- // may result in rather weird behavior if you have more than
- // one receiver (the highest priority receiver will get the
- // message from the highest priority sender, which may end up
- // seeming unpredictable). I don't recommend using multiple
- // receivers for mailboxes, priority or not.
- //------------------------------------------------------------
-
- class MBoxBase {
- private:
- int m_len; // length of msg
- void *m_msg; // ptr to msg data
- Sema s_pickup; // wait for receiver's pickup sema
- protected:
- SemaBase *s_send; // wait to send sema
- SemaBase *s_recv; // wait to receive sema
- public:
- // constructor
- MBoxBase()
- { }
- virtual ~MBoxBase()
- { delete s_send; delete s_recv; }
- // send a message to mailbox
- int send(void *msg, int n, clock_t msec = WaitForever);
- // receive a message from mailbox
- int recv(void *msg, clock_t msec = WaitForever);
- };
-
- class MBox: public MBoxBase {
- public:
- MBox()
- { s_send = new Sema(1); s_recv = new Sema; }
- };
-
- class MBoxPri: public MBoxBase {
- public:
- MBoxPri()
- { s_send = new SemaPri(1); s_recv = new SemaPri; }
- };
-
- //------------------------------------------------------------
- // ********** GENERIC CLASSES PIPE & CPIPE **********
- //
- // Classes Pipe and CPipe implement an in memory queue of
- // objects... there are no priorities associated with items.
- //
- // Class Pipe should be used only for C++ builtin data types,
- // or for structs/classes with no destructor (and the default
- // assignment operator).
- //
- // Class CPipe should be used for objects which have both
- // an assignment operator and an explicit destructor. Failure to do
- // so can result in all sorts of antisocial behavior, including
- // memory leaks (allocated blocks that will never be freed).
- // (Note: if you use a CPipe, the class MUST have a destructor,
- // because I call it explicitly when I take an object off the pipe.)
- //
- // To be placed on a CPipe, an object should have defined:
- //
- // 1. a default constructor with no args
- // 2. an assignment operator taking an argument of type "type&"
- // 3. an explicit destructor
- //
- // When receiving from the CPipe, you'll need a variable of the
- // appropriate type... it will have been initialized by the default
- // constructor. The receive function will destruct it before doing
- // the assignment. If you timeout on a receive, it will not have
- // been destroyed, so that we keep an even balance of constructors
- // (assignments) and destructors. (I know this is a little complex,
- // but I had to figure it out by experiment myself... it's not that
- // easy to figure out what calls the compiler is going to generate.)
- //
- // Declaration syntax is:
- //
- // (to generate data types)
- // declare(Pipe_, type);
- // declare(CPipe_, type);
- //
- // (to generate member functions)
- // implement(Pipe_, type);
- // implement(CPipe_, type);
- //
- // (to declare variables)
- // Pipe(type) var1;
- // CPipe(type) var2;
- //
- // Just as with mailboxes, I don't recommend multiple receivers
- // for pipes. It's rather hard to figure out what will go where.
- //------------------------------------------------------------
-
- #define Pipe(T) name2(Pipe_,T)
- #define CPipe(T) name2(CPipe_,T)
-
- #ifndef CPP_2_1
- // C++ 2.0 needs array size to delete
- #define P_MAX_ p_max
- #else
- // C++ 2.1 doesn't want array size for delete
- #define P_MAX_
- #endif
-
- // both Pipe and CPipe have the same data structures and interface
-
- #define PIPE_INTERNAL_(ptype,T) \
- private: \
- int p_max, p_head, p_tail; \
- Sema s_send, s_recv; \
- T *p_mem; \
- public: \
- ptype(int n): s_send(n) \
- { p_max = n; p_head = p_tail = 0; p_mem = new T [ n ]; } \
- virtual ~ptype() \
- { delete p_mem; } \
- int send(T &s, clock_t msec = WaitForever); \
- int operator<<(T &s) \
- { return send(s); } \
- int recv(T &s, clock_t msec = WaitForever); \
- int operator>>(T &s) \
- { return recv(s); }
-
- // the send function is the same for both Pipe and CPipe except
- // that for a CPipe, we destroy the current array element before
- // assigning the new value
-
- #define PIPE_SEND_INTERNAL_(dtorcall) \
- if (!s_send.wait(msec)) \
- return 0; \
- dtorcall; \
- p_mem[ p_tail++ ] = t; \
- if (p_tail == p_max) \
- p_tail = 0; \
- s_recv.signal(); \
- return 1;
-
- // the receive function is identical for Pipe and CPipe, except
- // for CPipe we destruct the target object before assigning to it
-
- #define PIPE_RECV_INTERNAL_(dtorcall) \
- if (!s_recv.wait(msec)) \
- return 0; \
- dtorcall; \
- t = p_mem[ p_head++ ]; \
- if (p_head == p_max) \
- p_head = 0; \
- s_send.signal(); \
- return 1;
-
- #define Pipe_declare(T) \
- class Pipe(T) { \
- PIPE_INTERNAL_(Pipe(T),T) \
- }
-
- #define Pipe_implement(T) \
- int Pipe(T)::send(T &t, clock_t msec) \
- { \
- PIPE_SEND_INTERNAL_((void)0) \
- } \
- int Pipe(T)::recv(T &t, clock_t msec) \
- { \
- PIPE_RECV_INTERNAL_((void)0) \
- }
-
- declare(Pipe_,uchar);
- declare(Pipe_,ushort);
-
- typedef Pipe(uchar) BPipe;
- typedef Pipe(ushort) WPipe;
-
- #define CPipe_declare(T) \
- class CPipe(T) { \
- PIPE_INTERNAL_(CPipe(T),T) \
- }
-
- #define CPipe_implement(T) \
- int CPipe(T)::send(T &t, clock_t msec) \
- { \
- PIPE_SEND_INTERNAL_(p_mem[ p_tail ].T::~T()) \
- } \
- int CPipe(T)::recv(T &t, clock_t msec) \
- { \
- PIPE_RECV_INTERNAL_(t.T::~T()) \
- }
-
- #endif
-