Category : C Source Code
Archive   : CTSK22.ZIP
Filename : TSKTTSK.C

 
Output of file : TSKTTSK.C contained in archive : CTSK22.ZIP
/*
--- Version 2.2 90-11-19 17:59 ---

TSKTTSK.C - CTask - Timer task.

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 2.1. The timer task functions were moved
from tsktimer to this module.

Timer logic has been significantly changed in version 2.0.
*/

#include "tsk.h"
#include "tsklocal.h"

/*
tsk_timer_action
performs the necessary action when a timeout occurred.
Starting with version 2.0, this part is not critical,
and may be preempted.

Version 2.1 makes this routine global for use by the keyboard
hotkey handler.
*/

void Localfunc tsk_timer_action (tlinkptr elem)
{
tcbptr task;
byte st;
CRITICAL;

switch (elem->struckind)
{
case TKIND_WAKE:
case TKIND_TASK: task = (tcbptr) elem->strucp;
C_ENTER;
st = task->state;

if (st == ST_WAITING || st == ST_DELAYED)
{
task->retptr =
(elem->link.kind == TYP_TIMER)
? TTIMEOUT
: TWATCH;
tsk_runable (task);
}
C_LEAVE;
break;

case TKIND_FLAG: set_flag ((flagptr) elem->strucp);
break;

case TKIND_COUNTER: inc_counter ((counterptr) elem->strucp);
break;

case TKIND_COUNTDEC: dec_counter ((counterptr) elem->strucp);
break;

case TKIND_PROC: tsk_callfunc (elem->strucp, elem);
break;

default: break;
}
}


/*
tsk_exec_watch
checks the watch condition, and returns 1 if the
condition is met.
*/

local int Staticfunc tsk_exec_watch (tlinkptr curr)
{
word val, cmp;
int elcmp;

elcmp = curr->elkind & 0x0f;

switch (curr->elkind & 0xf0)
{
case TELEM_MEM: val = *(curr->elem.mem.address) & curr->elem.mem.mask;
cmp = curr->elem.mem.compare;
if (elcmp == TCMP_CHG)
curr->elem.mem.compare = val;
break;

/* Microsoft C generates "Internal Compiler Error" on compiling
the following statement */
#if (TSK_TURBO)
case TELEM_PORT: val = (curr->elem.port.in_word)
? tsk_inpw (curr->elem.port.port)
: (word)tsk_inp (curr->elem.port.port);
#else
case TELEM_PORT: if (curr->elem.port.in_word)
val = tsk_inpw (curr->elem.port.port);
else
val = (word)tsk_inp (curr->elem.port.port);
#endif
val &= curr->elem.port.mask;
cmp = curr->elem.port.compare;
if (elcmp == TCMP_CHG)
curr->elem.port.compare = val;
break;

default: return 0;
}

switch (elcmp)
{
case TCMP_EQ: return val == cmp;
case TCMP_CHG:
case TCMP_NE: return val != cmp;
case TCMP_LE: return val <= cmp;
case TCMP_GE: return val >= cmp;
case TCMP_LES: return (int)val <= (int)cmp;
case TCMP_GES: return (int)val >= (int)cmp;
default: return 0;
}
}


/*
The timer task handles all timeouts.

Starting with version 2.0, two queues are maintained, one for
the timeouts, and one for watch elements.

The timeout queue now is ordered, with the tick count in the queue
element head holding the difference in ticks to the previous element.
Thus, only the first element of the timeout queue has to be counted
down, which will considerably speed up processing here if there are
multiple elements in the queue.

The watch queue is unordered.

The loop to check the queue elements is fully protected.
This allows other tasks access to the timer queue. The
concept used in pre-2.0 versions was pretty complicated to
handle, and still had to run with interupts disabled for
most parts.

The new logic unchains expired timeout elements immediately, within
the protected loop, but delays processing them until after the loop
is finished. Modification of those elements should not normally occur,
and will be rejected. The processing of the timeout/watch action can
thus be handled with interrupts enabled.

Version 2.1 corrects a bug that prevented wakeup elements from being
correctly processed if they had the repeat attribute. It introduces
a single link pointer in the timer control block for chaining the
elements to be acted upon, which is used exclusively by the timer
task. The 2.0 algorithm used the normal timer queue for chaining,
which convoluted the code, and, in the end, led to the mentioned bug.

Version 2.2 cleans up processing by eliminating the "state" field,
all necessary info is now contained in the flags. This better supports
enabling and disabling elements while the timer task is processing them.
*/

void Taskfunc tsk_timer (void)
{
queptr curr;
queptr help;
tlinkptr tlast;
tlinkptr tcurr;
byte flg;
CRITICAL;

while (1)
{
wait_counter_set (&tsk_timer_counter, 0L);

tlast = LNULL;

C_ENTER;

/* First, check the timeout queue. Since version 2.0, only
the first element has to be counted down.
*/

if (!((curr = GLOBDATA timer_queue.first)->kind & Q_HEAD))
if (!--curr->el.ticks)
{
/* Remove all counted-down elements from the timer queue,
marking them as not in queue and busy, and chaining them
through the special "next" pointer.
We can then process the elements outside the critical
section.
*/
do
{
help = curr;
curr = curr->next;
tsk_dequeue (help);
((tlinkptr)help)->flags |= TFLAG_BUSY;
((tlinkptr)help)->next = tlast;
tlast = (tlinkptr)help;
}
while (!(curr->kind & Q_HEAD) && !curr->el.ticks);
}

C_LEAVE;
/* shortly allow interrupts */
C_ENTER;

/* Now, check the watch queue. */

for (curr = GLOBDATA watch_queue.first; !(curr->kind & Q_HEAD); )
{
help = curr;
curr = curr->next;

if (tsk_exec_watch ((tlinkptr)help))
{
tsk_dequeue (help);
((tlinkptr)help)->flags |= TFLAG_BUSY;
((tlinkptr)help)->next = tlast;
tlast = (tlinkptr)help;
}
}

/* Ready checking the queues, we can now re-enable interrupts
for execution of the timeout/watch action. Interrupts are
disabled only for a short period to check the state and
re-enqueue repeat elements. */

C_LEAVE;

while (tlast != LNULL)
{
tcurr = tlast;
tlast = tlast->next;
tsk_timer_action (tcurr);

C_ENTER;
flg = tcurr->flags;
if (flg & TFLAG_REMOVE)
{
if (!(flg & TFLAG_TEMP))
flg &= ~TFLAG_REMOVE;
}
else if (!(flg & TFLAG_UNQUEUE))
{
if (flg & (TFLAG_REPEAT | TFLAG_ENQUEUE))
{
if (tcurr->link.kind == TYP_TIMER)
tsk_enqtimer ((queptr)tcurr, tcurr->elem.time.reload);
else
tsk_putqueue (&GLOBDATA watch_queue, (queptr)tcurr);
}
}
tcurr->flags &= ~(TFLAG_BUSY | TFLAG_UNQUEUE | TFLAG_ENQUEUE | TFLAG_REMOVE);
C_LEAVE;

#if (TSK_DYNAMIC)
if (flg & TFLAG_REMOVE)
{
#if (GROUPS)
tsk_dequeue ((queptr)&tcurr->chain);
#endif
tsk_pfree (tcurr);
}
#endif
}
}
}


/*
int8 is the timer interrupt chain task.
*/

#if (IBM && !INT8_EARLY)

void Taskfunc tsk_int8 (void)
{
while (1)
{
wait_counter_set (&tsk_int8_counter, 0L);
tsk_chain_timer ();
}
}

#endif



  3 Responses to “Category : C Source Code
Archive   : CTSK22.ZIP
Filename : TSKTTSK.C

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/