/* --- Version 2.2 90-10-12 15:47 --- TSKTASK.C - CTask - Task creation and deletion. CTask - a Multitasking Kernel for C Public Domain Software written by Thomas Wagner Ferrari electronic Gmbh Beusselstrasse 27 D-1000 Berlin 21 Germany No rights reserved. This file is new with version 2.1. Version 2.1 separates the functions previously collected in tskmain.c into tskmain.c - Front ends for installation/removal tskinst.c - Install/Remove main kernel tskgrp.c - Create/remove groups tsktask.c - Task creation and deletion tsktutl.c - Task utilities (get/set priority etc.) tskutil.c - General utilities (preemption, t_delay) */ #include "tsk.h" #include "tsklocal.h" /* tsk_use_ndp is initialized by install_tasker, and may be changed to 0 by the user to enable or disable ndp handling for tasks being created. Note that setting tsk_use_ndp to 1 will have no effect if install_tasker has determined that there is no NDP present, or if the NDP configuration flag is FALSE. */ int tsk_use_ndp = 0; #pragma check_stack(off) /* tsk_kill mark task as killed. CAUTION: Critical section assumed entered. */ local void Staticfunc tsk_kill (tcbptr task, int currtask) { /* Note: The following test shouldn't be necessary. However, if ever an interrupt handler should cause the current task to be killed while the scheduler is active, this would cause catastrophic effects (also see the notes in tskasm.asm). Well, no interrupt handler should ever do such a nasty thing, but... So if the in_sched flag is set, and the task is the current task, we have to stop everything. The test is ommitted when we're not running under DOS. */ #if (DOS) if (currtask && GLOBDATA in_sched) tsk_fatal ("Kill while in Scheduler"); #endif task->state = ST_KILLED; task->qhead = LNULL; tsk_deqtimer (&task->timerq.link); #if (TSK_NAMED) tsk_dequeue ((queptr)&task->name.list); #endif #if (TSK_DYNAMIC) if (!currtask) { if (task->flags & F_STTEMP) tsk_pfree (task->stkbot); /* Bug fixed in 2.1, was 'task->stack' */ if (task->flags & F_TEMP) tsk_pfree (task); } else if (task->flags & (F_STTEMP | F_TEMP)) { task->qhead = &GLOBDATA kill_queue; if (GLOBDATA kill_task->state == ST_STOPPED) tsk_runable (GLOBDATA kill_task); } #endif if (currtask) schedule (); } /* Killretn kills the current active task. It is used internally, but can also be called from outside. (However, *never* call it from an interrupt handler!) */ void Taskfunc killretn (void) { tsk_cli (); tsk_kill (GLOBDATA current_task, 1); } /* tsk_kill_queue Removes all tasks from a queue, and marks queue invalid. For internal use only. CAUTION: Critical section assumed entered. */ void Localfunc tsk_kill_queue (queheadptr que) { queptr curr, next; CHECK_QHEAD (que, "Task Kill Queue"); for (curr = que->first; !(curr->kind & Q_HEAD); ) { next = curr->next; tsk_kill ((tcbptr)curr, 0); curr = next; } tsk_init_qhead (que, 0xff); } /* ---------------------------------------------------------------------- */ /* create_task Initialises a tcb. The task is in stopped state initially. */ tcbptr Globalfunc create_task (tcbptr task, funcptr func, byteptr stack, word stksz, word prior, farptr arg TN(byteptr name) ) { struct task_stack far *stk; #if (CHECKING) word i; #endif #if (TSK_DYNAMIC) if (task == LNULL) { if ((task = (tcbptr) tsk_palloc (sizeof(tcb))) == LNULL) return LNULL; task->flags = F_TEMP; } else task->flags = 0; if (stack == LNULL) { if ((stack = (byteptr) tsk_palloc (stksz)) == LNULL) { if (task->flags & F_TEMP) tsk_pfree (task); return LNULL; } task->flags |= F_STTEMP; } #else task->flags = 0; #endif stk = (struct task_stack far *)(stack + stksz - sizeof (struct task_stack)); stk->r_ds = task->t_es = tsk_dseg (); stk->r_flags = tsk_flags (); stk->retn = func; stk->dummyret = killretn; stk->arg = arg; task->t_bp = 0; task->stkbot = stack; #if (CHECKING) for (i = 0; i < stksz - sizeof (struct task_stack); i++) *stack++ = (byte)i; #endif task->stack = (byteptr) stk; task->cqueue.kind = TYP_TCB; task->cqueue.next = LNULL; task->qhead = LNULL; task->state = ST_STOPPED; task->cqueue.el.pri.prior = task->cqueue.el.pri.ini_prior = prior; task->timerq.link.el.ticks = task->timerq.elem.time.reload = 0L; task->timerq.link.kind = TYP_TIMER; task->timerq.link.next = LNULL; task->timerq.strucp = (farptr) task; task->timerq.struckind = TKIND_WAKE; task->timerq.flags = 0; task->save_func = task->rest_func = LNULL; #if (DOS) task->sched_ent_func = LNULL; #endif task->user_ptr = LNULL; #if (GROUPS) task->group = task->homegroup = GLOBDATA current_task->group; #endif #if (DOS) task->indos = 0; task->t_new = 1; task->base_psp = task->group->create_psp; #endif #if (TSK_NAMED) tsk_add_name (&task->name, name, TYP_TCB, task); #endif #if (EMS) if (GLOBDATA ems_savetsk != LNULL) GLOBDATA ems_savetsk (task); #endif #if (NDP) if (tsk_use_ndp && GLOBDATA ndp_present) task->flags |= F_USES_NDP; #endif return task; } /* kill_task Removes a task from the system. */ void Globalfunc kill_task (tcbptr task) { CRITICAL; CHECK_TCBPTR (task, "Kill Task"); C_ENTER; if (task != GLOBDATA current_task) { if (task->state == ST_KILLED) return; else tsk_dequeue (&task->cqueue); tsk_kill (task, 0); } else tsk_kill (task, 1); C_LEAVE; } /* start_task Starts a stopped task. Returns -1 if the task was not stopped. */ int Globalfunc start_task (tcbptr task) { CRITICAL; if (task == LNULL) #if (GROUPS) task = GLOBDATA current_task->group->main_ptr; #else task = GLOBDATA main_ptr; #endif CHECK_TCBPTR (task, "Start Task"); if (task->state == ST_STOPPED) { task->state = ST_ELIGIBLE; C_ENTER; tsk_runable (task); C_LEAVE; return 0; } return -1; }