home *** CD-ROM | disk | FTP | other *** search
- /*****************************************************
- file: TASK.HPP Copyright 1989 by Dlugosz Software
- part of the multitasking classes.
- This is the core of the system.
- *****************************************************/
-
- #include "usual.hpp"
- #include <dos.h> //need FP_SEG, FP_OFf macros
- #include "task.hpp"
-
- task* active_task;
- task_list ready_list;
-
- /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
- /* task_head members */
- /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
-
- void task_head::add_to_head (task *p)
- {
- /* places the process p at the beginning of the list.
- If 'this' is a task (rather than a task_head), it places
- p after 'this' in the same list. */
- p->next= next;
- p->prev= this;
- next->prev= p;
- next= p;
- }
-
- /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
-
- void task_head::add_to_end (task *p)
- {
- /* places process p at the end of the list.
- If 'this' is a task, it places p before 'this in
- the same list. */
- p->next= this;
- p->prev= prev;
- prev->next= p;
- prev= p;
- }
-
- /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
-
- void task_head::unlink()
- {
- /* remove 'this' from its list. It is expected that 'this' is a
- member of a list, and not a head of a list. */
- prev->next= next;
- next->prev= prev;
- next= prev= this;
- }
-
- /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
-
- void task_head::zero()
- {
- /* set up this head to contain a list of zero elements. */
- next= prev= this;
- }
-
- /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
-
- task_list::~task_list()
- {
- task_iterator list (*this);
- task* p;
- while ((p= list.next()) != NULL)
- p->unblock(TRUE);
- }
-
- /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
-
- task* task_head::fetch_and_remove()
- {
- race_condition_cure _;
- task_head* t= next;
- if (t == this) return NULL; //list is empty
- t->unlink();
- return (task*)t;
- }
-
- /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
-
- bool task_list::resume_one()
- {
- task* t= fetch_and_remove();
- if (!t) return FALSE; //list was empty
- t->unblock();
- return TRUE;
- }
-
- /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
- /* task_iterator members */
- /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
-
- inline task* task_iterator::result()
- {
- if (current == start) return NULL;
- else return (task*)current;
- }
-
- /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
-
- task* task_iterator::next()
- {
- current= current->next;
- return result();
- }
-
- /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
-
- task* task_iterator::prev()
- {
- current= current->prev;
- return result();
- }
-
- /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
-
- task* task_iterator::same()
- {
- return result();
- }
-
- /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
- /* task helpers */
- /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
-
- static void fall_through()
- {
- /* this is the address that a detached function returns to. */
- active_task->flags |= killed;
- task_yield();
- /* never comes back */
- }
-
- /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
- /* task members */
- /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
-
- task::task (starter_function f, int priority, void* newstack, int stacksize, void* arg)
- {
- // first, set up stack to find arg and return address of detached function
- extern unsigned _DS;
-
- struct stack_setup {
- unsigned AX,BX,CX,DX,SI,DI; //values I don't care about
- unsigned BP;
- unsigned ES; //don't care
- unsigned DS;
- void* CSIP;
- unsigned Flags;
- /* all that is popped when returning. The following will be on
- the stack when the function starts up */
- void (*fallthrough)(); //psudo-return address
- void* argument;
- } *s;
-
- s= ((stack_setup*) (stacksize + (byte*)newstack)) -1;
-
- s->BP= 0;
- s->DS= _DS;
- s->CSIP= f;
- s->Flags= 0x246;
- s->fallthrough= fall_through;
- s->argument= arg;
- stack= s;
-
- //set up other fields
- priority_level= priority;
- //flags= 0; //one of these two lines should be
- flags= preemptive; //commented out, depending on how you what the default
- race_condition_cure _;
- add_to_list(ready_list); //ready to go
- }
-
- /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
-
- void task::kill()
- {
- { race_condition_cure _;
- if (iskilled()) return; //already did this
- flags |= killed;
- }
- unlink(); //remove from ready list or whatever waiting list
- if (this == active_task) task_yield(); //can't return!
- // and skip shutdown run
- if (!(flags & killable)) {
- // task must clean up itself
- flags &= ~preemptive;
- //let it execute shutdown code.
- ready_list.add_to_head(this); //it will run next
- task_yield();
- // it pops out in fall_through() or task_yield()
- }
- }
-
- /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
-
- void task::unblock (bool error)
- {
- race_condition_cure _;
- if (!isblocked()) return;
- #if defined(DEBUG)
- if (this == active_task) return;
- /* it could happen... If a task on the 'kill run' calls this
- function, it would put it back on the ready list and the system
- would crash when the task is run again. */
- #endif
- flags &= ~blocked;
- if (error) flags |= faulted;
- //remove from list it is now in (such as a semaphore waiting list)
- unlink();
- //and add to the ready list
- ready_list.add_to_end (this);
- }
-
- /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
-
- void task::block (task_list& l)
- {
- // block this task, and place it in list l
- flags |= blocked;
- { race_condition_cure _;
- /* this might be overkill-- I only have to make sure that the same list
- isn't being used in two places at once. However, I can't use a
- semaphore because this is used by the semaphore implementation, and
- callers cannot protect this function because this function does not
- return before yielding, when the active task is involved. */
- l.add_to_end (this);
- }
- if (this == active_task) //hey, that's me!
- task_yield(); // oh, well; I gotta do what he says.
- }
-
- /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
- /* non-members */
- /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
-
- void preemptable (bool flag)
- {
- preempt_off();
- if (active_task->flags & preemptive) {
- if (!flag) {
- //turning it off
- active_task->flags &=~preemptive;
- return; //preempt still disabled
- }
- }
- else {
- if (flag) {
- //turning it on
- active_task->flags |= preemptive;
- preempt_on();
- }
- }
- preempt_on();
- }
-
- /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
-
- static task* pick_task()
- {
- /* this function decides what the next task to run is */
- task_iterator i(ready_list);
- task* t= i.next(); //first in list, if any
- if (!t) return NULL; //none found
- t->unlink();
- return t;
- }
-
- /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
-
- void scheduler()
- {
- extern void* from_scheduler(void far* stack); //in assembly language
- extern void tasker_install(), tasker_remove();
-
- tasker_install();
- /* this is the main loop. It picks a task, runs it, and picks up agin
- when that task suspended. Then it picks another one. */
- for(;;) {
- active_task= pick_task(); //find one to run
- if (!active_task) break; //nothing to run, finished
- if (!(active_task->flags & preemptive)) preempt_off();
- active_task->stack= from_scheduler (active_task->stack);
- /* task runs */
- if (!(active_task->flags & preemptive)) preempt_on();
- if (!(active_task->flags&(blocked|killed)))
- active_task->add_to_list(ready_list);
- //I'm back. loop to the top/
- }
- tasker_remove();
- }
-
- /* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
-