home *** CD-ROM | disk | FTP | other *** search
/ Shareware Overload / ShartewareOverload.cdr / virus / ddj0491.zip / CPPTASK.ASC < prev    next >
Text File  |  1991-03-15  |  16KB  |  450 lines

  1. _COOPERATIVE MULTITASKING IN C++_
  2. by Marc Tarpenning
  3.  
  4. [LISTING ONE]
  5.  
  6. // File: TASK.H
  7. // Task object header -- Each task object contains its own stack, links to next
  8. // and previous task objects in the round robin, the saved stack pointer, and 
  9. // debug information.
  10.  
  11. class Task {
  12.     int *area;      // base stack location
  13.     int id;         // task id number for debuging purposes
  14.     Task *next;     // next task in chain
  15.     Task *prev;     // prev task in chain
  16.     int *saved;     // saved stack location during pause
  17.     int *top;       // top stack location
  18.  
  19. public:
  20.     void Activate(void (*)() );     // starting function
  21.     int GetId()                     // return id number
  22.         { return id; }
  23.     Task *GetNext()                 // return next pointer
  24.         { return next; }
  25.     int *GetSP()                    // return saved sp
  26.         { return saved; }
  27.     void SetNext(Task *t)           // sets next pointer
  28.         { next = t; }
  29.     void SetPrev(Task *t)           // sets prev pointer
  30.         { prev = t; }
  31.     void Show();                    // display data for debugging
  32.     void Switch();                  // context switch to next task
  33.     Task(int);                      // constructor
  34.     ~Task();                        // destructor
  35. };
  36.  
  37. Task *fork(void (*)() );            // forks tasks
  38. Task *InitTasking();                // Initializes all task stuff
  39. void pause();                       // switches to next task
  40.  
  41. extern int totalTasks;              // debug counter of total tasks
  42.  
  43.  
  44.  
  45. [LISTING TWO]
  46.  
  47. // File: TASK.CPP
  48. // Cooperative Multi-tasking in C++ -- 
  49. // Marc Tarpenning, P.O. 254801, Sacramento, CA  95865
  50. // Cis: 71435,1753    uucp: met@sactoh0.SAC.CA.US
  51.  
  52. #include <conio.h>
  53. #include <stdlib.h>
  54. #include "task.h"
  55.  
  56. // Protypes
  57. void terminate();               // terminate task on completion
  58.  
  59. // Defines
  60. #define STACKSIZE   0x200       // Stack size of each task
  61.  
  62. // Global variables
  63. Task *CurrentTask = 0;          // current task executing
  64. Task *SystemTask;               // main task using system stack
  65. int totalTasks = 0;             // debug counter of tasks
  66.  
  67. // Task.Activate - sets up the task object's stack so the when the task is
  68. // switched to, the passed function is performed.
  69. void Task::Activate(void (*f)() )
  70. {
  71.     saved = top;                    // reset stack
  72.     *(--saved) = (int) &terminate;  // kill task function on exit
  73.     *(--saved) = (int) f;           // place function for return address
  74.     *(--saved) = _BP;               // save all registers for switch
  75.     *(--saved) = _SI;               // save SI
  76.     *(--saved) = _DI;               // save DI
  77. }
  78.  
  79. // Task.Show - Debug information is displayed
  80. void Task::Show()
  81. {
  82.     cprintf("Task: %4i  area: %04X\n\r",id,area);
  83.     cprintf(" top: %04X saved: %04X\n\r",top,saved);
  84.     cprintf("prev: %04X  next: %04X",prev,next);
  85. }
  86.  
  87. // Task.Switch - switch context to next task object in the round robin.
  88. // It saves the current stack pointer, gets the stack pointer of the
  89. // next task (after making it current), and returns.
  90. void Task::Switch()
  91. {
  92.     _SI = _DI;                          // force compiler to save SI and DI
  93.     saved = (int *) _SP;                // store stack pointer
  94.     CurrentTask = next;                 // set current task to next task
  95.     _SP = (int) CurrentTask->saved;     // restore new task's stack pointer
  96. }
  97.  
  98. // Task.Task - Initializes the new task object.  Threads the object into
  99. // the linked round robin of other tasks.  If size is 0, then does not
  100. // allocate any stack space and uses the system stack instead (system).
  101. Task::Task(int size)
  102. {
  103.     static int newid = 0;       // unique identifier for each task
  104.     id = newid++;               // set ID and inc
  105.     totalTasks++;               // inc debug counter of total tasks
  106.     if (size) {                 // Want to create operator task?
  107.         if ((area = (int *) malloc(size * 2)) == 0)     // No, so allocate
  108.         {
  109.             cprintf("Not enough memory to create task %i\n", id);
  110.             exit(1);
  111.         }
  112.         top = area + size;              // set absolute top of stack
  113.         saved = top;                    // default saved stack to top
  114.  
  115.         next = CurrentTask->GetNext();  // link task in chain
  116.         prev = CurrentTask;
  117.         prev->SetNext(this);            // set current task to point to me
  118.         next->SetPrev(this);            // set next task to point to me
  119.     } else {                    // operator task, so don't allocate stack
  120.         top = (int *) _SP;              // instead, co-opt system stack
  121.         saved = top;
  122.         next = this;                    // since first task, make point
  123.         prev = this;                    // to myself
  124.     }
  125. }
  126.  
  127. // Task destructor - return all allocate memory to the system.
  128. Task::~Task()
  129. {
  130.     totalTasks--;           // dec debug counter of total tasks
  131.     prev->SetNext(next);    // unthread this task from the round robin.
  132.     next->SetPrev(prev);
  133.  
  134.     CurrentTask = next;     // Set new current task
  135.     if (area)               // don't free if no stack allocated (system)
  136.         free(area);         // free object's stack
  137. }
  138.  
  139. // fork -  creates a new task to execute the passed function.  When
  140. // the function has completed, the task is automatically destroyed.
  141. // fork returns a pointer to the new task object or NULL if out of memory.
  142. Task *fork(void (*f)() )
  143. {
  144.     Task *newtask;              // pointer to new task object
  145.     // In small memory models, malloc uses the stack pointer to
  146.     // determine if there is any free memory on the heap.
  147.     // To allow forking from sub-tasks, we "borrow" the system stack
  148.     // for the malloc operation.
  149.     int temp = _SP;             // save current stack pointer
  150.     _SP = (int) SystemTask->GetSP() - 20;   // borrow system stack
  151.     // create new task object
  152.     if ( (newtask = (Task *) new Task (STACKSIZE)) ) 
  153.         newtask->Activate(f);   // Setup new stack to execute function
  154.     _SP = temp;                 // restore original stack
  155.     return newtask;             // return a pointer to the new object
  156. }
  157.  
  158. // InitTasking - Initializes anything required before multitasking can
  159. // begin.  This function must be called before any other tasks are
  160. // forked.  It creates the "system" task by coopting the system
  161. // stack into a task object (task # 0).  It also sets CurrentTask
  162. // to point to the new operator task.
  163. Task *InitTasking()
  164. {
  165.     CurrentTask = (Task *) new Task(0);     // create system task
  166.     SystemTask = CurrentTask;   // set system task pointer
  167.     return SystemTask;          // return with pointer to system task
  168. }
  169.  
  170. // pause - non-object interface to switch context.
  171. void pause()
  172. {
  173.     CurrentTask->Switch();          // context switch out of current task
  174. }
  175.  
  176. // terminate - kills the current task when the fork is over.  This is not
  177. // a method, but its address is setup on the initial stack so if the
  178. // task's function ever returns, terminate will be the next execution addr.
  179. void terminate()
  180. {
  181.     _DI = _SI;                          // force compiler to save DI and SI
  182.     delete CurrentTask;                 // kill the current task
  183.     _SP = (int) CurrentTask->GetSP();   // set to next task's stack and
  184.                                         // return into the new task
  185. }
  186.  
  187.  
  188. [LISTING THREE]
  189.  
  190. // File: DEMO.CPP
  191. // Demo program for cooperative multitasking objects
  192. // Marc Tarpenning, P.O. 254801, Sacramento, CA  95865
  193. // Cis: 71435,1753    uucp: met@sactoh0.SAC.CA.US
  194.  
  195. // General includes for prototype headers
  196. #include <iostream.h>
  197. #include <stdlib.h>
  198. #include <stdio.h>
  199. #include <time.h>
  200. #include <conio.h>
  201. #include <string.h>
  202. #include <bios.h>
  203. #include <ctype.h>
  204. #include <dos.h>
  205.  
  206. #include "task.h"
  207. #include "twindow.h"
  208.  
  209. // Prototypes for simple demo functions
  210. void endlessCount();
  211. void fiveseconds();
  212. void funwindow();
  213. void msdelay(int);
  214. int  newgetch();
  215. void periodic();
  216. void quicky();
  217. void status();
  218. void wallclock();
  219.  
  220. main()
  221. {
  222.     /* Init multi-tasker.  Creates system parent task */
  223.     InitTasking();                  // init task, coopt system stack
  224.  
  225.     /* ---- Init screen ----- */
  226.     textattr(WHITE);                // set "normal" white on black
  227.     clrscr();                       // clear screen
  228.     _setcursortype(_NOCURSOR);      // kill cursor
  229.  
  230.     /* ----- start up some tasks ----- */
  231.     fork(&endlessCount);            // spawn endless counter task
  232.     fork(&wallclock);               // spawn clock task
  233.     fork(&periodic);                // spawn periodic launcher task
  234.     fork(&funwindow);               // spawn strange window
  235.     fork(&status);                  // spawn total number of tasks
  236.  
  237.     /* ----- create main window for user commands ---- */
  238.     TextWindow myWindow(1,20,80,25,(LIGHTGRAY<<4)+BLUE);
  239.     gotoxy(20,1);
  240.     cputs("*** Cooperative Multitasking Demo ***\r\n");
  241.     cputs("    Each one of the windows is a seperate task object ");
  242.     cputs("executing a C++\r\n");
  243.     cputs("    function.  All are running 'concurrently' using the ");
  244.     cputs("pause() context\r\n");
  245.     cputs("    switch routine.");
  246.     gotoxy(2,6);
  247.     cputs("Commands: [M]ake new task, [Q]uit");
  248.  
  249.     /* ----- wait for input & process key strokes ------ */
  250.     for (;;)
  251.         switch ( toupper( newgetch() ) ) {
  252.             case 'Q':               // quit - clean up screen and leave
  253.                 window(1,1,80,24);
  254.                 textattr(WHITE);
  255.                 clrscr();
  256.                 _setcursortype(_NORMALCURSOR);
  257.                 return(0);
  258.             case 'M':               // make - fork a new quick task
  259.                 fork(&quicky);
  260.                 break;
  261.             default:                // illegal character
  262.                 sound(500);
  263.                 msdelay(160);
  264.                 nosound();
  265.                 break;
  266.         }
  267. }
  268.  
  269. // endlessCount - opens a window and counts up forever.
  270. void endlessCount()
  271. {
  272.     TextWindow myWindow( 40,7,64,8,(CYAN<<4)+RED );
  273.     cprintf(" This task never ends!");
  274.     long count = 0;
  275.     for(;;) {                               // just keep counting, but
  276.         myWindow.Activate();                // don't forget to pause
  277.         gotoxy(1,2);
  278.         cprintf(" Current count: %li",count++);
  279.         pause();                            // let other tasks run
  280.     }
  281. }
  282.  
  283. // fiveseconds - opens a window, counts for 5 seconds, and returns
  284. void fiveseconds()
  285. {
  286.     TextWindow myWindow( 5,5,35,7,(GREEN<<4)+RED );     // make text window
  287.     cprintf("    This is a temporary task");
  288.     gotoxy(2,3);
  289.     cprintf("which only lasts five seconds");
  290.  
  291.     time_t t;                               // get current time
  292.     t = time(NULL);
  293.  
  294.     int i = 10000;                          // count down from 10000
  295.     while (difftime(time(NULL),t) < 5) {    // keep counting down until
  296.         myWindow.Activate();                // difftime is five seconds
  297.         gotoxy(13,2);                       // or more.
  298.         cprintf("%5i",i--);
  299.         pause();                            // let other tasks run
  300.     }
  301. }
  302.  
  303. // funwindow - displays moving character in window
  304. void funwindow()
  305. {
  306.     TextWindow myWindow(65,10,78,10, (BROWN<<4) + YELLOW);
  307.  
  308.     for(int i=0;;i = ++i % 20) {    // forever move i from 0 to 19
  309.         myWindow.Activate();
  310.         gotoxy( abs( ((i/10) * -20) + i) + 1 ,1);   // calc cursor
  311.         cputs(" * ");               // so range is 1..10 then 10..1
  312.         msdelay(100);               // delay ~ 100 ms
  313.     }
  314. }
  315.  
  316. // msdelay - delays the number of milliseconds with ~ 55ms resolution
  317. void msdelay(int delay)
  318. {
  319.     long ticksplus = biostime(0,0L) + delay / 55;
  320.     while (biostime(0,0L) < ticksplus)      // wait until time has passed
  321.         pause();                            // let other tasks run
  322. }
  323.  
  324. // newgetch - does same as getch, except calls pause while waiting
  325. // for a keyboard hit.
  326. int newgetch()
  327. {
  328.     while (!kbhit())
  329.         pause();
  330.     return getch();
  331. }
  332.  
  333. // periodic - occasionally launchs another task
  334. void periodic()
  335. {
  336.     TextWindow myWindow(1,10,41,11,(LIGHTGRAY<<4) + MAGENTA);
  337.     cputs(" Every ten seconds launch temporary task");
  338.     for (;;) {
  339.         for (int i=0; i < 10; i++) {    // loop ten times before forking
  340.             myWindow.Activate();
  341.             gotoxy(20,2);
  342.             cprintf("%i",i);        // display current count
  343.             msdelay(1000);          // delay ~ one second
  344.         }
  345.         fork(&fiveseconds);         // spawn new task which dies in 5 sec
  346.     }
  347. }
  348.  
  349. // quicky - opens window, hangs around for a few seconds, and leaves
  350. void quicky()
  351. {
  352.     static int xpos = 0;                // x position of new task window
  353.     static int ypos = 0;                // base y of new task window
  354.     TextWindow myWindow( xpos+1,ypos+12,xpos+16,ypos+12,(GREEN<<4)+BROWN);
  355.     xpos = (xpos+3) % 64;               // inc x position of "step" windows
  356.     ypos = ++ypos % 7;                  // inc y but keep within 7 lines
  357.  
  358.     for (int i=0; i < 10; i++) {        // count down for ten seconds
  359.         myWindow.Activate();
  360.         cprintf(" Dead in ten: %i",i);
  361.         msdelay(1000);                  // delay ~ one second
  362.     }
  363. }
  364.  
  365. // status - displays the number of tasks running
  366. void status()
  367. {
  368.     TextWindow myWindow(1,1,18,1, (CYAN<<4) + MAGENTA);
  369.     for (;;) {
  370.         myWindow.Activate();
  371.         cprintf(" Total tasks: %2i", totalTasks );      // display total
  372.         msdelay(200);               // delay ~ 200 ms
  373.     }
  374. }
  375.  
  376. // wallclock - continuously displays the current time
  377. void wallclock()
  378. {
  379.     TextWindow myWindow( 55,1,80,1, (LIGHTGRAY << 4) + BLUE);
  380.     time_t t;                       // will hold the current time
  381.     char buf[40];                   // temp buffer so can kill the \n
  382.     for (;;) {                      // always keep updating the time
  383.         myWindow.Activate();
  384.  
  385.         t = time(NULL);             // get the current time string address
  386.         strcpy(buf,ctime(&t));      // copy the string into temp
  387.         buf[24]='\0';               // kill the \n so window won't scroll
  388.         cprintf(" %s",buf);         // display it
  389.  
  390.         msdelay(1000);              // wait for ~ one second
  391.     }
  392. }
  393.  
  394.  
  395. [LISTING FIVE]
  396.  
  397. // File: TWINDOW.H -- Demo text window objects -- These window objects create 
  398. // a primitive text window for demo program. 
  399. // Assume Borland C++ libarary functions
  400.   
  401. class TextWindow {
  402.     int attrib;             // text mode attribute
  403.     int left,top;           // starting x and y position
  404.     int right,bottom;       // ending x and y position
  405.  
  406. public:
  407.     void Activate();                    // make active
  408.     TextWindow(int,int,int,int,int);    // constructor
  409.     ~TextWindow();                      // destructor
  410. };
  411.  
  412.  
  413.  
  414. [LISTING FIVE]
  415.  
  416. // File: TWINDOW.CPP -- Demo text window objects 
  417. // Marc Tarpenning, P.O. 254801, Sacramento, CA  95865
  418. // Cis: 71435,1753    uucp: met@sactoh0.SAC.CA.US
  419.  
  420. #include "twindow.h"
  421. #include <stdio.h>
  422. #include <conio.h>
  423.  
  424. // Window activation - makes this window the active window.
  425. void TextWindow::Activate()
  426. {
  427.     window(left,top,right,bottom);      // Use C++ library
  428.     textattr(attrib);                   // to make window active
  429. }
  430.  
  431. // Window constructor - store coordinates and clear window
  432. TextWindow::TextWindow(int l,int t,int r,int b,int a)
  433. {
  434.     left = l;           // set up all instance variables
  435.     top = t;
  436.     right = r;
  437.     bottom = b;
  438.     attrib = a;
  439.     Activate();         // activate window
  440.     clrscr();           // clear window
  441. }
  442.  
  443. // Window destructor - clears window with black background
  444. TextWindow::~TextWindow()
  445. {
  446.     Activate();
  447.     textattr( (BLACK << 4) + WHITE);
  448.     clrscr();
  449. }
  450.