home *** CD-ROM | disk | FTP | other *** search
- /* Kevo -- a prototype-based object-oriented language */
- /* (c) Antero Taivalsaari 1991-1993 */
- /* Some parts (c) Antero Taivalsaari 1986-1988 */
- /* tasks.c: Task management internals */
-
- #include "global.h"
-
- /*--------------------------------------------------------------------------*/
- /* Multitasking primitives */
-
- /*
- 'yieldTo()' changes the desired task to be executed right now.
- No checks are made whether the task is active, so you must be
- very careful in using this operation.
- */
- void yieldTo(thisTask)
- TASK** thisTask;
- {
- /* Push earlier environment to the top of the current return stack */
- storeExecEnv();
-
- up = thisTask; /* Task switch occurs here!! */
-
- /* Load new environment from the current return stack */
- loadExecEnv();
- }
-
-
- /* Store the current execution environment frame to the return stack */
- /*
- Note that since this operation changes the contents of the return
- stack, you cannot continue inner interpreter execution before
- executing the corresponding loadExecEnv() operation.
- */
- void storeExecEnv()
- {
- /* Depending on the machine architecture, select the faster implementation */
- nPushReturn(3);
- thirdReturn = (int*)contextSp;
- secondReturn = (int*)dataSp;
- topReturn = (int*)ip;
- (*up)->rpStore = returnSp;
- /*
- pushReturn((int*)contextSp);
- pushReturn((int*)dataSp);
- pushReturn((int*)ip);
- (*up)->rpStore = returnSp;
- */
- }
-
-
- /* Load the previous execution environment frame from the return stack */
-
- void loadExecEnv()
- {
- /* Depending on the machine architecture, select the faster implementation */
- returnSp = (*up)->rpStore;
- ip = (int**)topReturn;
- dataSp = secondReturn;
- contextSp = (int**)thirdReturn;
- nPopReturn(3);
- /*
- returnSp = (*up)->rpStore;
- ip = (int**)popReturn();
- dataSp = popReturn();
- contextSp = (int**)popReturn();
- */
- }
-
- /* buildTask(): build a new background task */
- /* The task shares the window with its creator */
-
- TASK** buildTask()
- {
- /*
- We copy the current task so the new task "inherits" all the
- properties from the current task.
- */
- TASK** newTask = (TASK**)copyObject(up);
- TASK** tempUp;
-
- /* New task needs its own execution stacks */
- (*newTask)->returnStack = copyObject((*up)->returnStack);
- (*newTask)->dataStack = copyObject((*up)->dataStack);
- (*newTask)->contextStack = copyObject((*up)->contextStack);
-
- /* New task has its own execution and textBuffer areas */
- (*newTask)->trampoline = copyObject((*up)->trampoline);
- (*newTask)->textBuffer = copyObject((*up)->textBuffer);
-
- /* New task has also its own file stacks */
- (*newTask)->infileStack = copyObject((*up)->infileStack);
- (*newTask)->outfileStack = copyObject((*up)->outfileStack);
-
- /* Initialize the text buffer */
- eraseKeyBuffer(newTask);
-
- /* Priority will be initialized to base priority */
- (*newTask)->priority = basePriority;
-
- /* Set the input and output files to 0 (terminal) */
- tempUp = up;
- up = newTask;
- infile = 0;
- outfile = 0;
- errfile = 0;
- infileSp = 0;
- outfileSp = 0;
- up = tempUp;
-
- /* Set the initial behavior of the task to be 'boot' */
- setTaskBehavior(newTask, oBoot);
-
- /* Update the task link and number of tasks */
- (*latestTask)->nextTask = newTask;
- (*newTask)->nextTask = NIL;
- latestTask = newTask;
- taskCount++;
-
- return(newTask);
- }
-
-
- /* previousRunningTask(): return the task that has its execution turn */
- /* right before the given task (is located in front of it in the RR-chain) */
-
- TASK** previousRunningTask(task)
- TASK** task;
- {
- TASK** thisTask = (*up)->nextInRobin;
- TASK** prevTask = up;
-
- /*
- Special case: If there is only one task in the round-robin chain,
- and that is the requested task, return it.
- */
- if (thisTask == prevTask) {
- if (task == thisTask) return(up);
- else return(FALSE);
- }
-
- while (thisTask != up) {
- if (task == thisTask) return(prevTask);
- prevTask = thisTask;
- thisTask = (*thisTask)->nextInRobin;
- }
-
- if (task == thisTask) return(prevTask);
- else return(FALSE);
- }
-
-
- /* isActivated(): check if a task is already in the round-robin chain */
-
- int isActivated(task)
- TASK** task;
- {
- TASK** thisTask = (*up)->nextInRobin;
-
- /*
- Special case: If there is only one task in the round-robin chain,
- and that is the requested task.
- */
- if (task == up) return(TRUE);
-
- while (thisTask != up) {
- if (task == thisTask) return(TRUE);
- thisTask = (*thisTask)->nextInRobin;
- }
-
- return(FALSE);
- }
-
-
- /* Activate a task, allowing the task to continue its previous execution */
- /* This operations operates only if the task is not already in the */
- /* round-robin chain. */
-
- void activateTask(thisTask)
- TASK** thisTask;
- {
- if (!isActivated(thisTask)) {
- TASK** temp = (*up)->nextInRobin;
- (*up)->nextInRobin = thisTask;
- (*thisTask)->nextInRobin = temp;
- runningCount++;
- }
- }
-
-
- /* Suspend a task (cancel its execution until it is reactivated) by */
- /* removing it from the round-robin chain. This operation operates */
- /* only if the task is in the round-robin chain, and the task is */
- /* not the only running task in the system (because otherwise */
- /* the system would die). */
-
- int suspendTask(thisTask)
- TASK** thisTask;
- {
- if (!isActivated(thisTask)) return(TRUE);
-
- if ((runningCount == 1) || (!multitasking && thisTask == up)) {
- /* Cannot suspend the only active task in the system */
- return(FALSE);
- }
- else {
- TASK** prevTask;
- prevTask = previousRunningTask(thisTask);
- (*prevTask)->nextInRobin = (*thisTask)->nextInRobin;
- runningCount--;
- return(TRUE);
- }
- }
-
-
- /* Suspend a task and yield to the next task automatically */
-
- int yieldingSuspend(thisTask)
- TASK** thisTask;
- {
- int success;
-
- if (runningCount == 1 && thisTask == up) return(FALSE);
-
- /* If singletasking and there are other active tasks available */
- if (!multitasking && thisTask == up && runningCount > 1) {
- yieldTo((*up)->nextInRobin);
- success = suspendTask(thisTask);
- }
- else {
- success = suspendTask(thisTask);
- yield();
- }
-
- return(success);
- }
-
-
- /* Reset the behavior of a task. The task must be in suspended state. */
- /* The code to be executed is given as an object (handle). */
-
- void setTaskBehavior(thisTask, behavior)
- TASK** thisTask;
- OBJECT* behavior;
- {
- TASK** tempUp = up;
- int* currentContext = topContext;
-
- if (isActivated(thisTask)) return;
-
- /*
- Change 'up' temporarily to 'thisTask' to allow easy initialization
- of new task's execution environment.
- */
- storeExecEnv();
- up = thisTask;
-
- /* Reset the stack pointers and set the instruction pointer (ip) */
- initStacks();
- ip = (int**)behavior->mfa;
-
- /* Assignment count of the task must be zero initially */
- (*thisTask)->assigning = 0;
-
- /*
- Context stack should normally never be empty.
- Therefore, we push the current task's context to the
- context stack of the new task.
- */
- pushContext(currentContext);
-
- /*
- Initialize the execution environment of the new task.
- 'Yield' will then load the environment when it is the new task's turn.
- */
- storeExecEnv();
-
- /* Finally, return to the current 'up' and execution environment. */
- up = tempUp;
- loadExecEnv();
- }
-
-
- /* Delete a task provided that it is not running */
-
- int deleteTask(thisTask)
- TASK** thisTask;
- {
- if (!isActivated(thisTask)) {
- /* Remove the task from the task list */
- if (thisTask == firstTask) firstTask = (*firstTask)->nextTask;
- else {
- TASK** prevTask = firstTask;
- while (prevTask) {
- if (thisTask == (*prevTask)->nextTask) {
- (*prevTask)->nextTask = (*thisTask)->nextTask;
- break;
- }
- prevTask = (*prevTask)->nextTask;
- }
- if (thisTask == latestTask && prevTask) latestTask = prevTask;
- }
-
- /* Release memory and decrement the task counter */
- deleteObject((OBJECT*)thisTask);
- taskCount--;
- return(TRUE); /* Task deleted successfully */
- }
- else return(FALSE); /* Task is currently running and cannot be deleted */
- }
-
-
- /*
- This is a stronger version of 'deleteTask' which will
- delete a task even if the task was currently running.
-
- At least one task must however remain running, because otherwise
- the system would crash.
- */
- int killTask(thisTask)
- TASK** thisTask;
- {
- if (yieldingSuspend(thisTask))
- return(deleteTask(thisTask));
- else return(FALSE); /* Cannot kill the only active task in the system */
- }
-
-
- /*
- These special stack operations are needed to allow the
- stacks of tasks to be initialized from other tasks.
- */
-
- /* Push a value to the data stack of the given task */
-
- void toTaskData(thisTask, data)
- TASK** thisTask;
- int data;
- {
- TASK** tempUp = up;
- yieldTo(thisTask);
- pushData(data);
- yieldTo(tempUp);
- }
-
-
- /* Push a value to the return stack of the given task */
- /* The task must not be active at the moment */
-
- void toTaskReturn(thisTask, data)
- TASK** thisTask;
- int data;
- {
- if (!isActivated(thisTask)) {
- TASK** tempUp = up;
- yieldTo(thisTask);
- pushReturn((int*)data);
- yieldTo(tempUp);
- }
- }
-
-
- /* Push a value to the context stack of the given task */
- /* The task must not be active at the moment */
-
- void toTaskCtxt(thisTask, data)
- TASK** thisTask;
- int data;
- {
- if (!isActivated(thisTask)) {
- TASK** tempUp = up;
- yieldTo(thisTask);
- pushContext((int*)data);
- yieldTo(tempUp);
- }
- }
-
-
- /* resizeDataStack(): resize the data stack of the given task */
- /* Since the location of stack's storage area may change, we must */
- /* preserve the stack pointer using an offset */
- /* The extra underflow area is added to the size */
- void resizeDataStack(thisTask, newSize)
- TASK** thisTask;
- int newSize;
- {
- OBJECT* stack = (*thisTask)->dataStack;
- TASK** tempUp = up;
- int ptrOffset;
-
- yieldTo(thisTask);
- ptrOffset = (int*)dataSp - (int*)stack->mfa;
- resizeClosure(stack, newSize + DATAOFFSET + UNDERFLOWRESERVE);
- dataSp = (int*)stack->mfa + ptrOffset;
- yieldTo(tempUp);
- }
-
-
- /* resizeReturnStack(): resize the return stack of the given task */
-
- void resizeReturnStack(thisTask, newSize)
- TASK** thisTask;
- int newSize;
- {
- OBJECT* stack = (*thisTask)->returnStack;
- TASK** tempUp = up;
- int ptrOffset;
-
- yieldTo(thisTask);
- ptrOffset = (int*)returnSp - (int*)stack->mfa;
- resizeClosure(stack, newSize + DATAOFFSET + UNDERFLOWRESERVE);
- returnSp = (int**)((int*)stack->mfa + ptrOffset);
- yieldTo(tempUp);
- }
-
-
- /* resizeContextStack(): resize the context stack of the given task */
-
- void resizeContextStack(thisTask, newSize)
- TASK** thisTask;
- int newSize;
- {
- OBJECT* stack = (*thisTask)->contextStack;
- TASK** tempUp = up;
- int ptrOffset;
-
- yieldTo(thisTask);
- ptrOffset = (int*)contextSp - (int*)stack->mfa;
- resizeClosure(stack, newSize + DATAOFFSET + UNDERFLOWRESERVE);
- contextSp = (int**)((int*)stack->mfa + ptrOffset);
- yieldTo(tempUp);
- }
-
-
- /* Get the "current working directory" (CWD) of the desired task */
- /* CWD = the bottommost value in the context stack (see 'global.h') */
-
- OBJECT* getTaskCWD(thisTask)
- TASK** thisTask;
- {
- OBJECT* self;
-
- TASK** tempUp = up;
- up = thisTask;
- self = (OBJECT*)CWD;
- up = tempUp;
- return(self);
- }
-
-
- /* Set the "current working directory" (CWD) of the desired task */
- /* CWD = the bottommost value in the context stack (see in 'global.h') */
-
- void setTaskCWD(thisTask, self)
- TASK** thisTask;
- OBJECT* self;
- {
- TASK** tempUp = up;
- up = thisTask;
- CWD = (int*)self;
- up = tempUp;
- }
-
-