home *** CD-ROM | disk | FTP | other *** search
/ Media Share 9 / MEDIASHARE_09.ISO / progmisc / ccl110je.zip / COROUTIN.CPP < prev    next >
C/C++ Source or Header  |  1993-06-19  |  10KB  |  341 lines

  1. //--------------------------------------------------------------------------
  2. //
  3. //      COROUTINE.CPP: body of DOS coroutine library.
  4. //      Copyright (c) J.English 1993.
  5. //      Author's address: je@unix.brighton.ac.uk
  6. //
  7. //      Permission is granted to use copy and distribute the
  8. //      information contained in this file provided that this
  9. //      copyright notice is retained intact and that any software
  10. //      or other document incorporating this file or parts thereof
  11. //      makes the source code for the library of which this file
  12. //      is a part freely available.
  13. //
  14. //--------------------------------------------------------------------------
  15. //
  16. //      Note: this library is highly DOS specific and hence non-portable.
  17. //      It also involves the use of assembly language and interrupt
  18. //      functions, so it is also very compiler-specific and will need
  19. //      modification for use with compilers other than Borland C++.
  20. //
  21. //      Revision history:
  22. //      1.0     April 1993      Initial coding
  23. //      1.1     June 1993       Added wait, terminate; tidied private data
  24. //                              Minor changes to schedule.
  25. //
  26. //--------------------------------------------------------------------------
  27.  
  28. #include "coroutine.h"
  29. #include <dos.h>
  30.  
  31.  
  32. //--------------------------------------------------------------------------
  33. //
  34. //      Constants.
  35. //
  36. const unsigned MIN_STACK  = 512;                // minimum stack size
  37.  
  38.  
  39. //--------------------------------------------------------------------------
  40. //
  41. //      Global (static) variables.
  42. //
  43. static CoroutineManager* current   = 0; // current coroutine
  44. static CoroutineManager* nextentry = 0; // current coroutine
  45. static Coroutine*        mainitem  = 0; // coroutine for execution of "main"
  46. static unsigned          mainsetup = 0; // flag set when constructing "main"
  47. static volatile unsigned count     = 0; // number of active coroutines
  48.  
  49.  
  50. //--------------------------------------------------------------------------
  51. //
  52. //      Class MainCoroutine.
  53. //
  54. //      A minimal derivation of Coroutine used for the main task.
  55. //
  56. class MainCoroutine : public Coroutine
  57. {
  58.   public:
  59.     MainCoroutine ()    : Coroutine (MIN_STACK)     { }
  60.     
  61.   protected:
  62.     virtual void main ()                            { }
  63. };
  64.  
  65. //--------------------------------------------------------------------------
  66. //
  67. //      Class CoroutineManager.
  68. //
  69. //      This is a support class used for maintaining the coroutine queue.
  70. //      The queue is represented as a circular list of CoroutineManagers.
  71. //
  72. class CoroutineManager
  73. {
  74.   public:
  75.     CoroutineManager (Coroutine* t);
  76.     ~CoroutineManager ()                  { delete stack; }
  77.  
  78.     CoroutineManager* next;               // next entry in list
  79.     CoroutineManager* prev;               // previous entry in list
  80.     Coroutine*        coroutine;          // coroutine for this entry
  81.     char*             stack;              // coroutine stack area
  82.     unsigned          sp,ss;              // coroutine stack pointer
  83.  
  84.     static void far start (Coroutine* t); // execute coroutine and then die
  85.  
  86.     static void create ();                // register coroutine creation
  87.     static void destroy ();               // register coroutine destruction
  88.  
  89.     static void interrupt schedule ();    // schedule next coroutine
  90. };
  91.  
  92.  
  93. //--------------------------------------------------------------------------
  94. //
  95. //      CoroutineManager::CoroutineManager.
  96. //
  97. //      Constructor for class CoroutineManager.
  98. //
  99. inline CoroutineManager::CoroutineManager (Coroutine* t)
  100. {
  101.     next = prev = this;
  102.     coroutine = t;
  103.     stack = 0;
  104. }
  105.  
  106.  
  107. //--------------------------------------------------------------------------
  108. //
  109. //      CoroutineManager::start.
  110. //
  111. //      This is used to execute a coroutine by executing "main" and
  112. //      then terminating it.
  113. //
  114. void far CoroutineManager::start (Coroutine* t)
  115. {
  116.     t->main ();
  117.     t->terminate ();
  118. }
  119.  
  120. //--------------------------------------------------------------------------
  121. //
  122. //      CoroutineManager::create.
  123. //
  124. //      Register the creation of a coroutine and initialise the system
  125. //      if it is the first coroutine to be created.
  126. //
  127. void CoroutineManager::create ()
  128. {
  129.     //--- increment the number of coroutines & initialise if necessary
  130.     if (count++ == 0)
  131.     {   
  132.         //--- create the main coroutine (bodged using "mainsetup")
  133.         mainsetup = 1;
  134.         mainitem = new MainCoroutine;
  135.         mainsetup = 0;
  136.         mainitem->state = Coroutine::RUNNING;
  137.  
  138.         //--- select main coroutine as the current one
  139.         current = mainitem->entry;
  140.     }
  141. }
  142.  
  143.  
  144. //--------------------------------------------------------------------------
  145. //
  146. //      CoroutineManager::destroy.
  147. //
  148. //      Register the destruction of a coroutine and stop scheduling if
  149. //      the last coroutine is being destroyed.
  150. //
  151. void CoroutineManager::destroy ()
  152. {
  153.     //--- decrement coroutine count
  154.     count--;
  155.  
  156.     //--- terminate main coroutine when no others left
  157.     if (count == 0)
  158.     {   current = 0;
  159.         mainitem->state = Coroutine::TERMINATED;
  160.         delete mainitem;
  161.     }
  162. }
  163.  
  164. //--------------------------------------------------------------------------
  165. //
  166. //      CoroutineManager::schedule.
  167. //
  168. //      Save the current coroutine and restore another one.
  169. //
  170. void interrupt CoroutineManager::schedule ()
  171. {
  172.     //--- don't schedule if no coroutines present
  173.     if (current == 0)
  174.         return;
  175.  
  176.     //--- switch context to "nextentry"
  177.     asm { cli; }
  178.     _SP = _BP;
  179.     current->sp = _SP;
  180.     current->ss = _SS;
  181.     current = nextentry;
  182.     _SS = current->ss;
  183.     _SP = current->sp;
  184.     _BP = _SP;
  185.     asm { sti; }
  186. }
  187.  
  188. //--------------------------------------------------------------------------
  189. //
  190. //      Coroutine::Coroutine.
  191. //
  192. //      Construct a new coroutine.  All new coroutines are kept in limbo
  193. //      until they are explicitly started using "run".
  194. //
  195. Coroutine::Coroutine (unsigned stacksize)
  196.     : entry (new CoroutineManager (this)),
  197.       state (TERMINATED)
  198. {
  199.     //--- leave coroutine terminated if coroutine manager not allocated
  200.     if (entry == 0)
  201.         return;
  202.  
  203.     //--- set up new coroutine (for all but main coroutine)
  204.     entry->stack = 0;
  205.     if (!mainsetup)
  206.     {   
  207.         //--- allocate coroutine stack area
  208.         if (stacksize < MIN_STACK)
  209.             stacksize = MIN_STACK;
  210.         entry->stack = new char [stacksize];
  211.         if (entry->stack == 0)
  212.             return;
  213.  
  214.         //--- register coroutine creation
  215.         CoroutineManager::create ();
  216.  
  217.         //--- create coroutine stack
  218.         unsigned* stk = (unsigned*)(entry->stack + stacksize);
  219.         *--(Coroutine**)stk = this;     // "this" pointer for call to "start"
  220.         stk -= 2;                       // dummy return address from "start"
  221.         *--stk = _FLAGS;                // flags
  222.         *--stk = FP_SEG (&CoroutineManager::start);
  223.         *--stk = FP_OFF (&CoroutineManager::start);
  224.         stk -= 5;                       // ax, bx, cx, dx, es
  225.         *--stk = _DS;                   // ds
  226.         stk -= 2;                       // si, di
  227.         *--stk = _BP;                   // bp
  228.         entry->ss = FP_SEG (stk);
  229.         entry->sp = FP_OFF (stk);
  230.  
  231.         //--- set coroutine state
  232.         state = CREATED;
  233.     }
  234. }
  235.  
  236. //--------------------------------------------------------------------------
  237. //
  238. //      Coroutine::~Coroutine.
  239. //
  240. //      Wait for current coroutine to terminate, and then destroy it.
  241. //
  242. Coroutine::~Coroutine ()
  243. {
  244.     //--- wait for coroutine to terminate
  245.     wait ();
  246.  
  247.     //--- register coroutine destruction (normal coroutines only)
  248.     if (this != mainitem)
  249.         CoroutineManager::destroy ();
  250.  
  251.     //--- delete associated structures
  252.     delete entry;
  253. }
  254.  
  255.  
  256. //--------------------------------------------------------------------------
  257. //
  258. //      Coroutine::run.
  259. //
  260. //      Start a new coroutine running.
  261. //
  262. int Coroutine::run ()
  263. {
  264.     //--- don't start coroutine if not newly created
  265.     if (state != CREATED)
  266.         return 0;
  267.  
  268.     //--- link coroutine into queue
  269.     entry->next = current->next;
  270.     entry->prev = current;
  271.     entry->prev->next = entry;
  272.     entry->next->prev = entry;
  273.  
  274.     //--- start coroutine running
  275.     state = RUNNING;
  276.     nextentry = entry;
  277.     CoroutineManager::schedule ();
  278.     return 1;
  279. }
  280.  
  281. //--------------------------------------------------------------------------
  282. //
  283. //      Coroutine::terminate.
  284. //
  285. //      This terminates a coroutine by removing it from the queue.  If
  286. //      a coroutine calls it to terminate itself, the next coroutine
  287. //      is selected and then scheduled.
  288. //
  289. void Coroutine::terminate ()
  290. {
  291.     //--- check if coroutine is running and reset its state
  292.     int running = (state == RUNNING);
  293.     state = TERMINATED;
  294.     if (!running)
  295.         return;
  296.  
  297.     //--- select next coroutine to run if necessary
  298.     if (entry == current)
  299.         nextentry = current->next;
  300.  
  301.     //--- detach coroutine from the queue
  302.     entry->next->prev = entry->prev;
  303.     entry->prev->next = entry->next;
  304.  
  305.     //--- schedule next coroutine if necessary
  306.     if (entry == current)
  307.         CoroutineManager::schedule ();
  308. }
  309.  
  310.  
  311. //--------------------------------------------------------------------------
  312. //
  313. //      Coroutine::wait.
  314. //
  315. //      Wait for a coroutine to terminate.  If it's the current coroutine
  316. //      waiting for itself to terminate, grant its wish by terminating it.
  317. //      If it hasn't been started yet, terminate it immediately.
  318. //
  319. void Coroutine::wait ()
  320. {
  321.     if (current == entry)
  322.         terminate ();
  323.     if (state == CREATED)
  324.         state = TERMINATED;
  325.     while (state != TERMINATED)
  326.         pause ();
  327. }
  328.  
  329.  
  330. //--------------------------------------------------------------------------
  331. //
  332. //      Coroutine::pause.
  333. //
  334. //      Schedule the next available coroutine.
  335. //
  336. void Coroutine::pause ()
  337. {
  338.     nextentry = current->next;
  339.     CoroutineManager::schedule ();
  340. }
  341.