/* TCSUB.C - TCtask Subroutines V1.1 T.Wagner V1.2 TECON Ltd. */ #include #include "tsk.h" #include "tclocal.h" /* tsk_enqueue inserts a task into a queue based on priority. */ void tsk_enqueue (tcbptr task, tqueptr que) { tcbptr last, curr; last = NULL; curr = *que; while (curr != NULL && curr->prior >= task->prior) { last = curr; curr = curr->next; } task->next = curr; if (last == NULL) *que = task; else last->next = task; task->queue = que; } /* tsk_unqueue Removes a task from somewhere in the middle of a queue. It is only used when stopping or prematurely waking a task, since in all other circumstances a task is only removed from the head of a queue. */ void tsk_unqueue (tcbptr task) { tcbptr last, curr; if (task->state == ST_RUNNING || task->queue == NULL) return; last = NULL; curr = *task->queue; while (curr != task) { if (curr == NULL) return; last = curr; curr = curr->next; } if (last == NULL) *task->queue = curr->next; else last->next = curr->next; task->queue = NULL; } /* tsk_enqtimer inserts a task into the timer queue. */ void tsk_enqtimer (tcbptr task, dword tout) { tlinkptr curr; if (tout == 0) return; /* Tasks are not sorted in the timer queue, so the task is inserted at the queue head. The timer task has to step through all tasks in the queue to cont down the timeout, so sorting would not bring any advantages. */ curr = &task->timerq; curr->timeout = tsk_timeout(tout); if (curr->tstate == TSTAT_IDLE) { curr->next = tsk_timer; tsk_timer = curr; } curr->tstate = TSTAT_COUNTDOWN; } /* tsk_runable make a task eligible for running. The task is removed from the timer queue and enqueued in the eligible queue. The old "next" pointer of the tcb is returned. This assumes that the task is removed from the head of a queue. */ tcbptr tsk_runable (tcbptr task) { tcbptr nxt; nxt = task->next; task->state = ST_ELIGIBLE; tsk_unqtimer (task); tsk_enqueue (task, &tsk_eligible); return nxt; } /* tsk_wakeup make a task eligible for running. The task is removed from the timer queue and enqueued in the eligible queue. This routine assumes that the task is removed from the middle of a queue. */ void tsk_wakeup (tcbptr task) { task->state = ST_ELIGIBLE; tsk_unqueue (task); tsk_unqtimer (task); tsk_enqueue (task, &tsk_eligible); } /* tsk_wait - put current running task in wait state. Note that the task is NOT enqueued in the respective queue here, this is done by the scheduler based on the queue head pointer. Only the timeout queue is affected directly. */ void tsk_wait (tqueptr que, dword timeout) { tsk_current->state = ST_WAITING; tsk_current->queue = que; tsk_enqtimer (tsk_current, timeout); schedule (); } /* tsk_kill - mark task as killed. */ void tsk_kill (tcbptr task) { #if (TSK_NAMED) tsk_del_name (&task->name); #endif task->state = ST_KILLED; if (task->timerq.tstate != TSTAT_IDLE) { task->timerq.tstate = (byte) TSTAT_REMOVE; #if (TSK_DYNAMIC) if (task->flags & F_TEMP) task->timerq.tkind |= TKIND_TEMP; } else { if (task->flags & F_STTEMP) tsk_free (task->stkbot); /* instead (task->stack) */ if (task->flags & F_TEMP) tsk_free (task); } #else } #endif } /* tsk_kill_queue Removes all tasks from a queue. For internal use only, critical section assumed entered. */ void tsk_kill_queue (tqueptr que) { tcbptr curr; for (curr = *que; curr != NULL; curr = curr->next) { tsk_unqtimer (curr); tsk_kill (curr); } *que = NULL; } #if (CLOCK_MSEC) dword tsk_timeout (dword tout) { dword t; t = (dword) (((double)tout / tick_factor) + 0.5); return (t) ? t : 1; } #endif #if (TSK_NAMED) /* tsk_add_name Initialise name-list element and insert it at the end of the list. NOTE: no check is made for duplicate names; names are not sorted. */ void tsk_add_name (nameptr elem, charptr name, byte kind, nearptr strucp) { charptr n; word_s i; CRITICAL; elem->nkind = kind; elem->strucp = strucp; n = elem->name; if (name != NULL) { for (i = 0; i < 8; i++) { if (name[i]) n[i]=name[i]; else break; } for (; i < 8; i++) n[i]=' '; } n[8] = 0; C_ENTER; elem->prev = tsk_name_list.prev; tsk_name_list.prev = elem; elem->follow = &tsk_name_list; elem->prev->follow = elem; //Адрес своего блока копирует по адресу //предыдущего блока в поле foolow, т.е. //становится следующим для предыдущего C_LEAVE; } /* tsk_del_name delete name-element from the name-list. */ void tsk_del_name (nameptr elem) { CRITICAL; C_ENTER; elem->follow->prev = elem->prev; elem->prev->follow = elem->follow; C_LEAVE; } /* find_name find structure, given name and type. If type is zero, the first name-element matching the name is returned. If type is nonzero, the first structure matching the name and type is returned. */ local word_s tsk_streq (charptr n1, charptr n2) { while (*n1 && *n1 == *n2) { n1++; n2++; } return *n1 == *n2; } nearptr find_name (charptr name, byte kind) { nameptr curr; for (curr = tsk_name_list.follow; curr->nkind; curr = curr->follow) if (!kind || curr->nkind == kind) if (tsk_streq (name, curr->name)) return (kind) ? curr->strucp : curr; return NULL; } #endif