home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 7 / 07.iso / c / c100 / 3.ddi / TASK.ZIP / TASK.CPP < prev    next >
Encoding:
C/C++ Source or Header  |  1989-08-28  |  7.5 KB  |  297 lines

  1. /*****************************************************
  2. file: TASK.HPP      Copyright 1989 by Dlugosz Software
  3.    part of the multitasking classes.
  4.    This is the core of the system.
  5. *****************************************************/
  6.  
  7. #include "usual.hpp"
  8. #include <dos.h>  //need FP_SEG, FP_OFf macros
  9. #include "task.hpp"
  10.  
  11. task* active_task;
  12. task_list ready_list;
  13.  
  14. /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
  15. /*      task_head members                   */
  16. /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
  17.  
  18. void task_head::add_to_head (task *p)
  19. {
  20. /* places the process p at the beginning of the list.
  21.    If 'this' is a task (rather than a task_head), it places
  22.    p after 'this' in the same list.    */
  23. p->next= next;
  24. p->prev= this;
  25. next->prev= p;
  26. next= p;
  27. }
  28.  
  29. /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
  30.  
  31. void task_head::add_to_end (task *p)
  32. {
  33. /* places process p at the end of the list.
  34.    If 'this' is a task, it places p before 'this in
  35.    the same list.    */
  36. p->next= this;
  37. p->prev= prev;
  38. prev->next= p;
  39. prev= p;
  40. }
  41.  
  42. /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
  43.  
  44. void task_head::unlink()
  45. {
  46. /* remove 'this' from its list.  It is expected that 'this' is a
  47.    member of a list, and not a head of a list.  */
  48. prev->next= next;
  49. next->prev= prev;
  50. next= prev= this;
  51. }
  52.  
  53. /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
  54.  
  55. void task_head::zero()
  56. {
  57. /* set up this head to contain a list of zero elements. */
  58. next= prev= this;
  59. }
  60.  
  61. /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
  62.  
  63. task_list::~task_list()
  64. {
  65. task_iterator list (*this);
  66. task* p;
  67. while ((p= list.next()) != NULL)
  68.    p->unblock(TRUE);
  69. }
  70.  
  71. /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
  72.  
  73. task* task_head::fetch_and_remove()
  74. {
  75. race_condition_cure _;
  76. task_head* t= next;
  77. if (t == this) return NULL;  //list is empty
  78. t->unlink();
  79. return (task*)t;
  80. }
  81.  
  82. /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
  83.  
  84. bool task_list::resume_one()
  85. {
  86. task* t= fetch_and_remove();
  87. if (!t) return FALSE;  //list was empty
  88. t->unblock();
  89. return TRUE;
  90. }
  91.  
  92. /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
  93. /*     task_iterator members                */
  94. /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
  95.  
  96. inline task* task_iterator::result()
  97. {
  98. if (current == start) return NULL;
  99. else return (task*)current;
  100. }
  101.  
  102. /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
  103.  
  104. task* task_iterator::next()
  105. {
  106. current= current->next;
  107. return result();
  108. }
  109.  
  110. /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
  111.  
  112. task* task_iterator::prev()
  113. {
  114. current= current->prev;
  115. return result();
  116. }
  117.  
  118. /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
  119.  
  120. task* task_iterator::same()
  121. {
  122. return result();
  123. }
  124.  
  125. /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
  126. /*     task helpers                         */
  127. /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
  128.  
  129. static void fall_through()
  130. {
  131. /* this is the address that a detached function returns to.  */
  132. active_task->flags |= killed;
  133. task_yield();
  134.    /* never comes back */
  135. }
  136.  
  137. /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
  138. /*     task members                         */
  139. /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
  140.  
  141. task::task (starter_function f, int priority, void* newstack, int stacksize, void* arg)
  142. {
  143. // first, set up stack to find arg and return address of detached function
  144. extern unsigned _DS;
  145.  
  146. struct stack_setup {
  147.    unsigned AX,BX,CX,DX,SI,DI;  //values I don't care about
  148.    unsigned BP;
  149.    unsigned ES;  //don't care
  150.    unsigned DS;
  151.    void* CSIP;
  152.    unsigned Flags;
  153.    /* all that is popped when returning.  The following will be on
  154.       the stack when the function starts up  */
  155.    void (*fallthrough)();  //psudo-return address
  156.    void* argument;
  157.    }  *s;
  158.  
  159. s= ((stack_setup*) (stacksize + (byte*)newstack)) -1;
  160.  
  161. s->BP= 0;
  162. s->DS= _DS;
  163. s->CSIP= f;
  164. s->Flags= 0x246;
  165. s->fallthrough= fall_through;
  166. s->argument= arg;
  167. stack= s;
  168.  
  169. //set up other fields
  170. priority_level= priority;
  171. //flags= 0;          //one of these two lines should be
  172. flags= preemptive;   //commented out, depending on how you what the default
  173. race_condition_cure _;
  174. add_to_list(ready_list);  //ready to go
  175. }
  176.  
  177. /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
  178.  
  179. void task::kill()
  180. {
  181. {  race_condition_cure _;
  182.    if (iskilled()) return;  //already did this
  183.    flags |= killed;
  184.    }
  185. unlink();  //remove from ready list or whatever waiting list
  186. if (this == active_task) task_yield();  //can't return!
  187.    // and skip shutdown run
  188. if (!(flags & killable)) {
  189.    // task must clean up itself
  190.    flags &= ~preemptive;
  191.    //let it execute shutdown code.
  192.    ready_list.add_to_head(this);  //it will run next
  193.    task_yield();
  194.       // it pops out in fall_through() or task_yield()
  195.    }
  196. }
  197.  
  198. /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
  199.  
  200. void task::unblock (bool error)
  201. {
  202. race_condition_cure _;
  203. if (!isblocked()) return;
  204. #if defined(DEBUG)
  205. if (this == active_task) return;
  206.    /* it could happen... If a task on the 'kill run' calls this
  207.       function, it would put it back on the ready list and the system
  208.       would crash when the task is run again. */
  209. #endif
  210. flags &= ~blocked;
  211. if (error) flags |= faulted;
  212. //remove from list it is now in (such as a semaphore waiting list)
  213. unlink();
  214. //and add to the ready list
  215. ready_list.add_to_end (this);
  216. }
  217.  
  218. /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
  219.  
  220. void task::block (task_list& l)
  221. {
  222. // block this task, and place it in list l
  223. flags |= blocked;
  224. {  race_condition_cure _;
  225.    /* this might be overkill-- I only have to make sure that the same list
  226.       isn't being used in two places at once.  However, I can't use a
  227.       semaphore because this is used by the semaphore implementation, and
  228.       callers cannot protect this function because this function does not
  229.       return before yielding, when the active task is involved.  */
  230.    l.add_to_end (this);
  231.    }
  232. if (this == active_task) //hey, that's me!
  233.    task_yield();  // oh, well; I gotta do what he says.
  234. }
  235.  
  236. /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
  237. /*   non-members                            */
  238. /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
  239.  
  240. void preemptable (bool flag)
  241. {
  242. preempt_off();
  243. if (active_task->flags & preemptive) {
  244.    if (!flag) {
  245.       //turning it off
  246.       active_task->flags &=~preemptive;
  247.       return;  //preempt still disabled
  248.       }
  249.    }
  250. else {
  251.    if (flag) {
  252.       //turning it on
  253.       active_task->flags |= preemptive;
  254.       preempt_on();
  255.       }
  256.    }
  257. preempt_on();
  258. }
  259.  
  260. /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
  261.  
  262. static task* pick_task()
  263. {
  264. /* this function decides what the next task to run is */
  265. task_iterator i(ready_list);
  266. task* t= i.next();  //first in list, if any
  267. if (!t) return NULL;  //none found
  268. t->unlink();
  269. return t;
  270. }
  271.  
  272. /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
  273.  
  274. void scheduler()
  275. {
  276. extern void* from_scheduler(void far* stack);  //in assembly language
  277. extern void tasker_install(), tasker_remove();
  278.  
  279. tasker_install();
  280. /* this is the main loop.  It picks a task, runs it, and picks up agin
  281.    when that task suspended.  Then it picks another one.   */
  282. for(;;) {
  283.    active_task= pick_task();  //find one to run
  284.    if (!active_task) break;  //nothing to run, finished
  285.    if (!(active_task->flags & preemptive)) preempt_off();
  286.    active_task->stack= from_scheduler (active_task->stack);
  287.             /* task runs */
  288.    if (!(active_task->flags & preemptive)) preempt_on();
  289.    if (!(active_task->flags&(blocked|killed)))
  290.       active_task->add_to_list(ready_list);
  291.    //I'm back.  loop to the top/
  292.    }
  293. tasker_remove();
  294. }
  295.  
  296. /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
  297.