# /* task.c */ /* EMACS_MODES: c !fill */ /* This file contains the routines which implement the UNIX multitasking * system. Only the (hopefully) machine-independent routines are included * herein; the machine-dependent routines are in the file task.s. The * following routines are included: * tk_init initialize the tasking system * tk_fork start executing a new task * tk_new set up task control block * tk_exit stop executing current task * tk_block block the current task * tk_sched the task scheduler */ #include #include #include "task.h" #define stk_init(siz) (calloc (1, sizeof (task))) #define stk_alloc(siz) (calloc (1, (siz))) task *tk_init (stksiz, extsiz) /* Initialize the tasking system. Create the first task, running it on * the main process stack, after initializing the stack allocation system. * The task is created at the default priority, with a stack of size * stksiz and an external data area of size extsiz. The routine returns * to the caller with the caller running as the first task. Returns * the task's "handle" - a pointer to the task control block. * * Arguments: */ int stksiz; /* stack size in bytes */ int extsiz; /* external static size in bytes */ { register task *tk; /* new task */ tk = (task *)stk_init (stksiz + sizeof (task)); tk_new (tk, TK_RUN, extsiz); /* set up task control block */ tk_cur = tk; /* make it current task */ return (tk); } task *tk_fork (start, stksiz, extsiz) /* Fork a new task, with entry point start, stksiz bytes of stack, and * extsiz bytes of external static storage. The task is created ready * to run, on the runnable queue; its initial * stack frame is set up to enter it at the specified start address. * This routine does not affect the execution of the currently running * task. Return a handle for the new task. * * Arguments: */ int (*start) (); /* entry address of task */ int stksiz; /* task's stack size */ int extsiz; /* task's extern storage size */ { register task *tk; /* pointer to task ctl block */ tk = (task *)stk_alloc (stksiz + sizeof (task)); tk_frame (tk, &tk->tk_stack[(stksiz / sizeof (stack))], start); tk_new (tk, TK_RDY, extsiz); /* init other fields */ q_addt (&tk_rdy, tk); /* add to ready queue */ return (tk); } tk_new (tk, state, extsiz) /* Initialize the task control block for a task starting in the specified * state. Allocate the specified amount of external storage. * * Arguments: */ register task *tk; /* task control block addr */ unsigned state; int extsiz; { extern caddr_t calloc (); tk->tk_elt.qe_next = NULL; /* no next task */ tk->tk_state = state; tk->tk_evf = 0; /* no outstanding events */ if (extsiz != 0) tk->tk_extern = calloc (1, extsiz); /* set up statics */ else tk->tk_extern = NULL; } tk_sched () /* This is the scheduler for the tasking system. It must scan the queue * of all blocked tasks from the top, looking for tasks which have * awakened and moving them to the ready queue. It then tries to * dequeue the first task from the ready queue, which is maintained * in FIFO order. If it fails, the scheduler causes the process * to block using the sysleep () call. If it succeeds, it calls * tk_swtch () to switch to the context of the new task. tk_swtch () * will save the current task's context and return in the context of * the new task. */ { register task **temp; /* temp for chaining */ register task *tk; /* next task to run */ tk_resched = 0; for (;;) { for (temp = &tk_blk.tq_head; *temp != NULL; ) { tk = *temp; if (tst_and_clr (&tk->tk_evf)) { /* awake? */ q_dela (&tk_blk, tk, temp); tk->tk_state = TK_RDY; q_addt (&tk_rdy, tk); } else temp=(task **)(((task *)temp)->tk_elt.qe_next); } if ((tk = (task *)q_deq (&tk_rdy)) != NULL) /* any tasks? */ break; while (!tst_and_clr (&tk_resched)) /* block process */ sysleep (TK_SYSLEEP); } tk->tk_state = TK_RUN; tk_swtch (tk); /* switch to new task */ }