home *** CD-ROM | disk | FTP | other *** search
/ Black Box 4 / BlackBox.cdr / progc / aetsk100.arj / TASK.H < prev    next >
C/C++ Source or Header  |  1991-12-01  |  16KB  |  489 lines

  1. /**********************************************************************
  2.  *  
  3.  *  NAME:           task.h
  4.  *  
  5.  *  DESCRIPTION:    header for multi-tasking scheduler
  6.  *  
  7.  *  copyright (c) 1991 J. Alan Eldridge
  8.  * 
  9.  *  Warning: because of a bug in the Borland C++ clock() function,
  10.  *  timeouts may not work correctly when crossing midnight boundaries.
  11.  *
  12.  *  M O D I F I C A T I O N   H I S T O R Y
  13.  *
  14.  *  when        who                 what
  15.  *  -------------------------------------------------------------------
  16.  *  03/12/91    J. Alan Eldridge    created
  17.  *  
  18.  *  Mar-May 91  J. Alan Eldridge    many, many revisions
  19.  *
  20.  *  10/23/91    JAE                 added code to force task stack size 
  21.  *                                  up to reasonable minimum value
  22.  *
  23.  *  10/26/91    JAE                 added support for DJ Delorie's port
  24.  *                                  port of GNU C++ v. 1.39
  25.  *
  26.  *  11/09/91    JAE                 rewrote to use SysQP class for 
  27.  *                                  Sema, Task queues
  28.  *
  29.  *                                  tasks now can have priorities...
  30.  *                                  (be careful of starvation!)
  31.  *
  32.  *  11/11/91    JAE                 added stack basher checking in class
  33.  *                                  Task, called by scheduler
  34.  *
  35.  *  11/13/91    JAE                 removed obsolete queue types
  36.  *
  37.  *                                  Semas can now be prioritized
  38.  *                                  by Task priority (so the highest
  39.  *                                  priority Task gets its wait() first)
  40.  *                                  ... MBoxes also have this feature
  41.  *
  42.  *  11/16/91    jae                 rewrote pipes to use generic.h <arrgh!>
  43.  *                                  (I just can't maintain two sets of source,
  44.  *                                  one with templates and one without... when
  45.  *                                  everybody is using C++ 3.0, I'll replace
  46.  *                                  the generics with templates.)
  47.  *                                  
  48.  *  11/21/91    jae                 added class ChildTask (so you can create
  49.  *                                  a task from within another task, and
  50.  *                                  then wait for it to finish before 
  51.  *                                  continuing)
  52.  *
  53.  *  11/30/91    jae                 added fDelete flag to Task, so you can
  54.  *                                  dynamically create a new Task on the heap,
  55.  *                                  and then proceed, knowing that the storage
  56.  *                                  will eventually be freed when the task
  57.  *                                  is killed or commits sucicide 
  58.  *
  59.  *********************************************************************/
  60.  
  61. #ifndef __TASK_H
  62. #define __TASK_H
  63.  
  64. #include    <generic.h>
  65.  
  66. #define SUSPEND_ON_WAIT 0   //  if 1, suspend on Sema.wait() even if
  67.                             //  resource is available (if 0, don't suspend)
  68.  
  69. #if !defined(__TURBOC__) && !defined(__GNUG__)
  70. #error  Turbo C++/Borland C++ or GNU C++ required!
  71. #endif
  72.  
  73. class Task; //  forward decl for typedefs
  74.  
  75. #include    "sysqvfnc.h"
  76.  
  77. #define GetNxtTask(q)   ((Task*)((q).Get()))
  78.  
  79. //  Task++ version number
  80.  
  81. #define TASKPP_VERSION  "3.10"
  82.  
  83. //------------------------------------------------------------
  84. //  ********** SCHEDULER **********
  85. //
  86. //  the scheduler has only 1 public entry point:
  87. //  scheduler(arg)  --  starts up the tasks (arg != 0)
  88. //                  --  shuts down all tasks (arg == 0)
  89. //------------------------------------------------------------
  90.  
  91. extern int  scheduler(int tasking = 1);
  92.  
  93. //------------------------------------------------------------
  94. //  a ptr to the currently executing task is kept in CurrTask
  95. //------------------------------------------------------------
  96.  
  97. class Task; //  forward reference
  98.  
  99. extern Task *CurrTask;
  100.  
  101. //------------------------------------------------------------
  102. //  everything that takes a wait time argument can also give
  103. //  one of these values
  104. //------------------------------------------------------------
  105.  
  106. #define NoWait      0L
  107. #define WaitForever -1L
  108.  
  109. //------------------------------------------------------------
  110. //  ********** CLASS SEMAPHORE **********
  111. //
  112. //  semaphores are used to control access to resources,
  113. //  such as the keyboard, a printer, etc.
  114. //------------------------------------------------------------
  115.  
  116. class SemaBase {
  117. private:
  118.     int             s_value;        //  resource count
  119. protected:
  120.     SysQP           *s_waiting;     //  queue of tasks waiting
  121. public:
  122.     SemaBase(int val = 0): s_value(val) { }
  123.     ~SemaBase()
  124.         { delete s_waiting; }
  125.     //  check value
  126.     int     avail()
  127.         { return s_value > 0; }
  128.     operator int()  //  as in: if (sema) ...
  129.         { return avail(); }
  130.     //  wait on sema
  131.     int     wait(clock_t msec = WaitForever);
  132.     void    operator--()
  133.         { wait(); }
  134. #ifdef  CPP_2_1
  135.     void    operator--(int i)
  136.         { wait(); }
  137. #endif
  138.     //  signal sema
  139.     void    signal(int susp = 1);
  140.     void    operator++()
  141.         { signal(); }
  142. #ifdef  CPP_2_1
  143.     void    operator++(int i)
  144.         { signal(); }
  145. #endif
  146. };
  147.  
  148. class Sema: public SemaBase {
  149. public:
  150.     Sema(int val=0): SemaBase(val)
  151.         { s_waiting = new SysQP; }
  152. };
  153.  
  154. class SemaPri: public SemaBase {
  155. public:
  156.     SemaPri(int val=0): SemaBase(val)
  157.         { s_waiting = new SysPriQP; }
  158. };
  159.  
  160. //------------------------------------------------------------
  161. //  ********** CLASS TASK **********
  162. //
  163. //  class Task implements the basic schedulable entity...
  164. //------------------------------------------------------------
  165.  
  166. class Task: public Qable {
  167. private:
  168.     friend class    SemaBase;
  169.     friend int      scheduler(int tasking);
  170.     friend void     SchWakeup();
  171.     
  172.     static const uchar  stkval; //  value to fill stack with
  173.  
  174.     char        *tskname;       //  name of task
  175.  
  176.     //  flags for task state
  177.     uint        fInited:1,      //  initialized
  178.                 fReady:1,       //  ready to run
  179.                 fTimed:1,       //  blocked w/timeout
  180.                 fZombie:1,      //  dead but not buried
  181.                 fDelete:1;      //  should delete when dead
  182.  
  183.     jmp_buf     tskEnv;         //  environment for context switching
  184.     int         stklen;         //  length of task's stack
  185.     uchar       *stack;         //  this task's stack space
  186.     clock_t     wakeUp;         //  when to wake up if asleep
  187.     int         tskpri;         //  task priority
  188.  
  189.     //  get state of flags
  190.     int     isInited()
  191.         { return fInited; }
  192.     int     isReady()
  193.         { return fReady; }
  194.     int     isTimed()
  195.         { return fTimed; }
  196.     int     isZombie()
  197.         { return fZombie; }
  198.     int     shouldDelete()
  199.         { return fDelete; }
  200.  
  201.     //  clear fTimed flag and return previous value
  202.     int     timeOut();
  203.     //  set up stack and begin execution
  204.     void    init();
  205.     //  return to place of last suspend()
  206.     void    resume(int code = 1)
  207.         { longjmp(tskEnv, code); }
  208.     //  possibly wake up a timed task
  209.     int     maybeWake();
  210.     //  if blocked, task will wait until another unblocks it
  211.     int     block(clock_t msec = WaitForever);
  212.     //  make task ready to execute again
  213.     void    unblock();
  214.     //  check if stack bashed
  215.     int     stackok();
  216. public:
  217.     //  enum for minimum allowed stack size
  218. #if     defined(__TURBOC__)
  219. #if     defined(_Windows)
  220.     enum { StackMin = 0x2000 };
  221. #else
  222.     enum { StackMin = 0x1000 };
  223. #endif
  224. #elif   defined(__GNUG__)
  225.     enum { StackMin = 0x2000 };
  226. #endif
  227.     //  constructor, destructor
  228.     Task(char *tname, int stk = StackMin);
  229.     ~Task()
  230.         { delete stack; }
  231.     //  get name of task
  232.     char *name()
  233.         { return tskname; }
  234.     //  set the delete flag
  235.     void    setDelete()
  236.         { fDelete = 1; }
  237.     //  shut down one task
  238.     virtual void    suicide();
  239.     //  the main routine for each task
  240.     virtual void    TaskMain() = 0;
  241.     //  voluntarily give up control
  242.     int     suspend();
  243.     //  wait a specified amount of time
  244.     void    sleep(clock_t msec)
  245.         { block(msec); }
  246.     //  get, set priority
  247.     int     priority()
  248.         { return tskpri; }
  249.     int     priority(int newpri)
  250.         { int oldpri = tskpri; tskpri = newpri; return oldpri; }
  251.     //  compare tasks for priority
  252.     int     operator<(Qable &t)
  253.         { return tskpri < ((Task*)(&t))->tskpri; }
  254. };
  255.  
  256. //  these functions are for calls from non-member functions ...
  257. //  they are strictly for convenience in avoiding the CurrTask-> syntax
  258.  
  259. inline char *TaskName()             { return CurrTask->name(); }
  260. inline void TaskSuicide()           { CurrTask->suicide(); }
  261. inline int  TaskSuspend()           { return CurrTask->suspend(); }
  262. inline void TaskSleep(clock_t msec) { CurrTask->sleep(msec); }
  263. inline int  TaskPriority()          { return CurrTask->priority(); }
  264. inline int  TaskPriority(int newpri){ return CurrTask->priority(newpri); }
  265.  
  266. //  kill an arbitrary task, including the current one
  267. inline void TaskKill(Task *t)       { t->suicide(); }
  268.  
  269. //  report a fatal error and die with a call to exit(1)
  270. //  NOTE: the user is free to replace this routine if desired
  271. void        TaskFatal(char *fmt, ...);
  272.  
  273. //------------------------------------------------------------
  274. //  ********** CLASS CHILDTASK **********
  275. //
  276. //  this is a class that implements a child task: the parent waits
  277. //  for the child to die before continuing...
  278. //
  279. //  e.g.:
  280. //
  281. //  ChildTaskDerived    *pcht;      //  ptr to child task
  282. //
  283. //  pcht = new ChildTaskDerived("MyChild"); //  create new task
  284. //  pcht->wait();   //  wait for new task to die
  285. //  delete pcht;    //  destroy new task (now that it's dead)
  286. //------------------------------------------------------------
  287.  
  288. class ChildTask: public Task, public Sema {
  289. public:
  290.     ChildTask(char *tname, int stk=StackMin): Task(tname,stk)
  291.         { }
  292.     virtual void    suicide()
  293.         { signal(0); Task::suicide(); }
  294. };
  295.  
  296. //------------------------------------------------------------
  297. //  ********** CLASS MBOX **********
  298. //
  299. //  class MBox implements a mailbox: this is an intertask
  300. //  communication device that implements a rendezvous type
  301. //  of interaction; that is, a send must block until a receiver
  302. //  has picked up the message. A sender or receiver may specify
  303. //  a timeout period.
  304. //
  305. //  Note that class MBoxPri (a mailbox with priority semaphores)
  306. //  may result in rather weird behavior if you have more than
  307. //  one receiver (the highest priority receiver will get the
  308. //  message from the highest priority sender, which may end up
  309. //  seeming unpredictable). I don't recommend using multiple
  310. //  receivers for mailboxes, priority or not.
  311. //------------------------------------------------------------
  312.  
  313. class MBoxBase {
  314. private:
  315.     int             m_len;      //  length of msg
  316.     void            *m_msg;     //  ptr to msg data
  317.     Sema            s_pickup;   //  wait for receiver's pickup sema
  318. protected:
  319.     SemaBase        *s_send;    //  wait to send sema
  320.     SemaBase        *s_recv;    //  wait to receive sema
  321. public:
  322.     //  constructor
  323.     MBoxBase()
  324.         { }
  325.     ~MBoxBase()
  326.         { delete s_send; delete s_recv; }
  327.     //  send a message to mailbox
  328.     int     send(void *msg, int n, clock_t msec = WaitForever);
  329.     //  receive a message from mailbox
  330.     int     recv(void *msg, clock_t msec = WaitForever);
  331. };
  332.  
  333. class MBox: public MBoxBase {
  334. public:
  335.     MBox()
  336.         { s_send = new Sema(1); s_recv = new Sema; }
  337. };
  338.  
  339. class MBoxPri: public MBoxBase {
  340. public:
  341.     MBoxPri()
  342.         { s_send = new SemaPri(1); s_recv = new SemaPri; }
  343. };
  344.  
  345. //------------------------------------------------------------
  346. //  ********** GENERIC CLASSES PIPE & CPIPE **********
  347. //
  348. //  Classes Pipe and CPipe implement an in memory queue of
  349. //  objects... there are no priorities associated with items.
  350. //
  351. //  Class Pipe should be used only for C++ builtin data types,
  352. //  or for structs/classes with no destructor (and the default
  353. //  assignment operator).
  354. //
  355. //  Class CPipe should be used for objects which have both
  356. //  an assignment operator and an explicit destructor. Failure to do
  357. //  so can result in all sorts of antisocial behavior, including
  358. //  memory leaks (allocated blocks that will never be freed).
  359. //  (Note: if you use a CPipe, the class MUST have a destructor,
  360. //  because I call it explicitly when I take an object off the pipe.)
  361. //
  362. //  To be placed on a CPipe, an object should have defined:
  363. //
  364. //  1. a default constructor with no args
  365. //  2. an assignment operator taking an argument of type "type&"
  366. //  3. an explicit destructor
  367. //
  368. //  When receiving from the CPipe, you'll need a variable of the
  369. //  appropriate type... it will have been initialized by the default
  370. //  constructor. The receive function will destruct it before doing
  371. //  the assignment. If you timeout on a receive, it will not have
  372. //  been destroyed, so that we keep an even balance of constructors
  373. //  (assignments) and destructors. (I know this is a little complex,
  374. //  but I had to figure it out by experiment myself... it's not that
  375. //  easy to figure out what calls the compiler is going to generate.)
  376. //
  377. //  Declaration syntax is:
  378. //
  379. //  (to generate data types)
  380. //  declare(Pipe_, type);
  381. //  declare(CPipe_, type);
  382. //
  383. //  (to generate member functions)
  384. //  implement(Pipe_, type);
  385. //  implement(CPipe_, type);
  386. //
  387. //  (to declare variables)
  388. //  Pipe(type)  var1;
  389. //  CPipe(type) var2;
  390. //
  391. //  Just as with mailboxes, I don't recommend multiple receivers
  392. //  for pipes. It's rather hard to figure out what will go where.
  393. //------------------------------------------------------------
  394.  
  395. #define Pipe(T)     name2(Pipe_,T)
  396. #define CPipe(T)    name2(CPipe_,T)
  397.  
  398. #ifndef CPP_2_1
  399. //  C++ 2.0 needs array size to delete
  400. #define P_MAX_ p_max
  401. #else
  402. //  C++ 2.1 doesn't want array size for delete
  403. #define P_MAX_
  404. #endif
  405.  
  406. //  both Pipe and CPipe have the same data structures and interface
  407.  
  408. #define PIPE_INTERNAL_(ptype,T) \
  409. private: \
  410.     int     p_max, p_head, p_tail; \
  411.     Sema    s_send, s_recv; \
  412.     T       *p_mem; \
  413. public: \
  414.     ptype(int n): s_send(n) \
  415.         { p_max = n; p_head = p_tail = 0; p_mem = new T [ n ]; } \
  416.     ~ptype() \
  417.         { delete [ P_MAX_ ] p_mem; } \
  418.     int     send(T &s, clock_t msec = WaitForever); \
  419.     int     operator<<(T &s) \
  420.         { return send(s); } \
  421.     int     recv(T &s, clock_t msec = WaitForever); \
  422.     int     operator>>(T &s) \
  423.         { return recv(s); } 
  424.  
  425. //  the send function is the same for both Pipe and CPipe except
  426. //  that for a CPipe, we destroy the current array element before
  427. //  assigning the new value
  428.  
  429. #define PIPE_SEND_INTERNAL_(dtorcall) \
  430.     if (!s_send.wait(msec)) \
  431.         return 0; \
  432.     dtorcall; \
  433.     p_mem[ p_tail++ ] = t; \
  434.     if (p_tail == p_max) \
  435.         p_tail = 0; \
  436.     s_recv.signal(); \
  437.     return 1;
  438.  
  439. //  the receive function is identical for Pipe and CPipe, except
  440. //  for CPipe we destruct the target object before assigning to it
  441.  
  442. #define PIPE_RECV_INTERNAL_(dtorcall) \
  443.     if (!s_recv.wait(msec)) \
  444.         return 0; \
  445.     dtorcall; \
  446.     t = p_mem[ p_head++ ]; \
  447.     if (p_head == p_max) \
  448.         p_head = 0; \
  449.     s_send.signal(); \
  450.     return 1;
  451.  
  452. #define Pipe_declare(T) \
  453. class Pipe(T) { \
  454.     PIPE_INTERNAL_(Pipe(T),T) \
  455. }
  456.         
  457. #define Pipe_implement(T) \
  458. int Pipe(T)::send(T &t, clock_t msec) \
  459. { \
  460.     PIPE_SEND_INTERNAL_((void)0) \
  461. } \
  462. int Pipe(T)::recv(T &t, clock_t msec) \
  463. { \
  464.     PIPE_RECV_INTERNAL_((void)0) \
  465. }
  466.  
  467. declare(Pipe_,uchar);
  468. declare(Pipe_,ushort);
  469.  
  470. typedef Pipe(uchar)     BPipe;
  471. typedef Pipe(ushort)    WPipe;
  472.  
  473. #define CPipe_declare(T) \
  474. class CPipe(T) { \
  475.     PIPE_INTERNAL_(CPipe(T),T) \
  476. }
  477.  
  478. #define CPipe_implement(T) \
  479. int CPipe(T)::send(T &t, clock_t msec) \
  480. { \
  481.     PIPE_SEND_INTERNAL_(p_mem[ p_tail ].T::~T()) \
  482. } \
  483. int CPipe(T)::recv(T &t, clock_t msec) \
  484. { \
  485.     PIPE_RECV_INTERNAL_(t.T::~T()) \
  486. }
  487.  
  488. #endif
  489.