home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Frostbyte's 1980s DOS Shareware Collection
/
floppyshareware.zip
/
floppyshareware
/
DOOG
/
CTASK.ZIP
/
TSKTIMER.C
< prev
next >
Wrap
C/C++ Source or Header
|
1989-12-21
|
17KB
|
712 lines
/*
--- Version 2.0 89-12-15 14:57 ---
TSKTIM.C - CTask - Timer routines.
CTask - a Multitasking Kernel for C
Public Domain Software written by
Thomas Wagner
Patschkauer Weg 31
D-1000 Berlin 33
West Germany
No rights reserved.
This file is new with 1.2. The timer related functions were moved
from tskmain to this module.
Timer logic has been significantly changed in version 2.0.
*/
#include <stdio.h>
#include "tsk.h"
#include "tsklocal.h"
#include <stdarg.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.
*/
local void near tsk_timer_action (tlinkptr elem)
{
tcbptr task;
byte st;
CRITICAL;
switch (elem->link.kind)
{
/* Note: TKIND_WAKE handled in task body */
case TKIND_TASK: task = (tcbptr) elem->strucp;
C_ENTER;
st = task->state;
if (st == ST_WAITING || st == ST_DELAYED)
{
task->retptr =
((elem->elkind & 0xf0) == TELEM_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_PROC: ((funcptr_void_fp) elem->strucp)(elem);
break;
default: break;
}
}
/*
tsk_exec_watch
checks the watch condition, and returns 1 if the
condition is met.
*/
local int near 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 (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 (except for task waits)
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.
*/
void far tsk_timer (void)
{
queptr curr;
queptr help;
queptr tlast;
byte state;
CRITICAL;
while (1)
{
wait_counter_set (&tsk_timer_counter, 0L);
tlast = NULL;
C_ENTER;
/* First, check the timeout queue. In version 2.0, only
the first element has to be counted down.
*/
if ((curr = GLOBDATA timer_queue.first)->kind)
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 "prev" pointer.
We can then process the elements outside the critical
section.
The only hairy situation could arise if a task is
waiting for an event with timeout, the timeout
hits, we take it off the queue, are preempted, and
the task is again made waiting. This would kill our
chain. So this special case is handled here.
*/
do
{
help = curr;
curr = curr->next;
tsk_dequeue (help);
if (help->kind == TKIND_WAKE)
{
((tcbptr)((tlinkptr)help)->strucp)->retptr = TTIMEOUT;
tsk_runable (((tlinkptr)help)->strucp);
}
else
{
((tlinkptr)help)->flags |= TFLAG_BUSY;
help->prev = tlast;
tlast = help;
}
}
while (curr->kind && !curr->el.ticks);
}
/* Now, check the watch queue. */
for (curr = GLOBDATA watch_queue.first; curr->kind; )
{
help = curr;
curr = curr->next;
if (tsk_exec_watch ((tlinkptr)help))
{
if (help->kind == TKIND_WAKE)
{
((tcbptr)((tlinkptr)help)->strucp)->retptr = TWATCH;
tsk_runable (((tlinkptr)help)->strucp);
}
else
{
tsk_dequeue (help);
((tlinkptr)help)->flags |= TFLAG_BUSY;
help->prev = tlast;
tlast = 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 != NULL)
{
curr = tlast;
tlast = tlast->prev;
tsk_timer_action ((tlinkptr)curr);
C_ENTER;
state = ((tlinkptr)curr)->tstate;
if (state == TSTAT_REPEAT)
tsk_enqtimer (curr, ((tlinkptr)curr)->elem.time.reload);
else if (state == TSTAT_CONTWATCH)
tsk_putqueue (&GLOBDATA watch_queue, curr);
else
state = ((tlinkptr)curr)->tstate = TSTAT_IDLE;
((tlinkptr)curr)->flags &= ~TFLAG_BUSY;
C_LEAVE;
#if (TSK_DYNAMIC)
if (state == TSTAT_IDLE && ((tlinkptr)curr)->flags & TFLAG_TEMP)
tsk_free (curr);
#endif
}
}
}
/*
int8 is the timer interrupt chain task.
*/
#if (IBM)
void far tsk_int8 (void)
{
while (1)
{
wait_counter_set (&tsk_int8_counter, 0L);
tsk_chain_timer ();
}
}
#endif
/* ---------------------------------------------------------------------- */
/*
t_delay
delay the current task by "ticks" clock ticks.
If ticks is zero, the task is stopped.
*/
int far t_delay (dword ticks)
{
tcbptr task = GLOBDATA current_task;
tsk_cli ();
tsk_dequeue (&task->cqueue);
tsk_deqtimer (&task->timerq.link);
task->qhead = NULL;
if (ticks)
{
task->state = ST_DELAYED;
task->timerq.link.kind = TKIND_WAKE;
task->timerq.tstate = TSTAT_COUNTDOWN;
task->timerq.elkind = TELEM_TIMER;
tsk_enqtimer (&task->timerq.link, ticks);
}
else
task->state = ST_STOPPED;
schedule ();
return (int)task->retptr;
}
/*
setup_telem
Initializes a timeout/watch element.
*/
local tlinkptr near tsk_setup_telem (tlinkptr elem, farptr strucp,
byte kind, dword upar)
{
if (kind <= TKIND_TASK || kind > TKIND_COUNTER)
return NULL;
#if (TSK_DYNAMIC)
if (elem == NULL)
{
if ((elem = tsk_alloc (sizeof (tlink))) == NULL)
return NULL;
elem->flags = TFLAG_TEMP;
}
else
elem->flags = 0;
#else
elem->flags = 0;
#endif
elem->link.kind = kind;
elem->strucp = strucp;
elem->user_parm = upar;
return elem;
}
/*
create_timer
Creates a timer queue timeout element. The element is inserted into
the timeout queue.
*/
tlinkptr far create_timer (tlinkptr elem, dword tout, farptr strucp,
byte kind, byte rept, ...)
{
va_list val;
CRITICAL;
va_start (val, rept);
elem = tsk_setup_telem (elem, strucp, kind, va_arg (val, dword));
if (elem == NULL)
return NULL;
elem->tstate = (byte)((rept) ? TSTAT_REPEAT : TSTAT_COUNTDOWN);
elem->elkind = TELEM_TIMER;
tout = elem->elem.time.reload = tsk_timeout(tout);
if (tout)
{
C_ENTER;
tsk_enqtimer (&elem->link, tout);
C_LEAVE;
}
va_end (val);
return elem;
}
/*
delete_timer
Deletes a timeout element.
*/
void far delete_timer (tlinkptr elem)
{
CRITICAL;
C_ENTER;
if (elem->flags & TFLAG_BUSY)
{
elem->tstate = (byte)TSTAT_REMOVE;
C_LEAVE;
return;
}
tsk_deqtimer (&elem->link);
C_LEAVE;
#if (TSK_DYNAMIC)
if (elem->flags & TFLAG_TEMP)
tsk_free (elem);
#endif
}
/*
change_timer
Changes the timeout and/or repeat-flag in a timer element.
If the timer was idle, it is inserted into the timeout queue.
If 0 is passed as timeout, the element is removed from the
timeout queue (same as delete_timer).
This routine can *not* be used for dynamically allocated
timer elements.
*/
void far change_timer (tlinkptr elem, dword tout, byte rept, ...)
{
va_list val;
CRITICAL;
if (!tout)
{
delete_timer (elem);
return;
}
if (elem->flags & TFLAG_TEMP)
return;
va_start (val, rept);
C_ENTER;
elem->user_parm = va_arg (val, dword);
tout = elem->elem.time.reload = tsk_timeout(tout);
elem->tstate = (byte)((rept) ? TSTAT_REPEAT : TSTAT_COUNTDOWN);
if (!(elem->flags & TFLAG_BUSY))
{
tsk_deqtimer (&elem->link);
if (tout)
tsk_enqtimer (&elem->link, tout);
}
C_LEAVE;
va_end (val);
}
/*
create_memory_watch
Creates a timer queue memory watch element. The element is
inserted into the watch queue.
*/
tlinkptr far create_memory_watch (tlinkptr elem, farptr address,
word mask, word compare, byte cmpkind,
farptr strucp, byte kind, byte rept, ...)
{
va_list val;
CRITICAL;
if (cmpkind < TCMP_EQ || cmpkind > TCMP_CHG)
return NULL;
va_start (val, rept);
elem = tsk_setup_telem (elem, strucp, kind, va_arg (val, dword));
if (elem == NULL)
return NULL;
elem->elkind = (byte)(TELEM_MEM | cmpkind);
elem->tstate = (byte)((rept) ? TSTAT_CONTWATCH : TSTAT_WATCH);
elem->elem.mem.address = address;
elem->elem.mem.mask = mask;
elem->elem.mem.compare = compare;
C_ENTER;
tsk_putqueue (&GLOBDATA watch_queue, &elem->link);
C_LEAVE;
va_end (val);
return elem;
}
/*
create_port_watch
Creates a timer queue port watch element. The element is
inserted into the timeout queue.
*/
tlinkptr far create_port_watch (tlinkptr elem, word port, byte in_word,
word mask, word compare, byte cmpkind,
farptr strucp, byte kind, byte rept, ...)
{
va_list val;
CRITICAL;
if (cmpkind < TCMP_EQ || cmpkind > TCMP_CHG)
return NULL;
va_start (val, rept);
elem = tsk_setup_telem (elem, strucp, kind, va_arg (val, dword));
if (elem == NULL)
return NULL;
elem->elkind = (byte)(TELEM_PORT | cmpkind);
elem->tstate = (byte)((rept) ? TSTAT_CONTWATCH : TSTAT_WATCH);
elem->elem.port.port = port;
elem->elem.port.in_word = in_word;
elem->elem.port.mask = mask;
elem->elem.port.compare = compare;
C_ENTER;
tsk_putqueue (&GLOBDATA watch_queue, &elem->link);
C_LEAVE;
va_end (val);
return elem;
}
/*
wait_port
Delay current task until specified port watch condition is met.
*/
int far wait_port (word port, byte in_word,
word mask, word compare, byte cmpkind)
{
tlinkptr elem;
tcbptr task = GLOBDATA current_task;
if (cmpkind < TCMP_EQ || cmpkind > TCMP_CHG)
return 1;
elem = &task->timerq;
elem->link.kind = TKIND_WAKE;
elem->elkind = (byte)(TELEM_PORT | cmpkind);
elem->tstate = (byte)(TSTAT_WATCH);
elem->elem.port.port = port;
elem->elem.port.in_word = in_word;
elem->elem.port.mask = mask;
elem->elem.port.compare = compare;
tsk_cli ();
task->state = ST_DELAYED;
task->qhead = NULL;
tsk_putqueue (&GLOBDATA watch_queue, &elem->link);
schedule ();
return (int)task->retptr;
}
/*
wait_memory
Delay current task until specified memory watch condition is met.
*/
int far wait_memory (farptr address,
word mask, word compare, byte cmpkind)
{
tlinkptr elem;
tcbptr task = GLOBDATA current_task;
if (cmpkind < TCMP_EQ || cmpkind > TCMP_CHG)
return 1;
elem = &task->timerq;
elem->link.kind = TKIND_WAKE;
elem->elkind = (byte)(TELEM_MEM | cmpkind);
elem->tstate = (byte)(TSTAT_WATCH);
elem->elem.mem.address = address;
elem->elem.mem.mask = mask;
elem->elem.mem.compare = compare;
tsk_cli ();
task->state = ST_DELAYED;
task->qhead = NULL;
tsk_putqueue (&GLOBDATA watch_queue, &elem->link);
schedule ();
return (int)task->retptr;
}
/*
delete_watch
Deletes a watch element.
*/
void far delete_watch (tlinkptr elem)
{
CRITICAL;
C_ENTER;
if (elem->flags & TFLAG_BUSY)
{
elem->tstate = (byte)TSTAT_REMOVE;
C_LEAVE;
return;
}
tsk_dequeue (&elem->link);
C_LEAVE;
#if (TSK_DYNAMIC)
if (elem->flags & TFLAG_TEMP)
tsk_free (elem);
#endif
}
/*
create_ticker
link element into ticker chain.
*/
tick_ptr far create_ticker (tick_ptr elem, dword val)
{
CRITICAL;
#if (TSK_DYNAMIC)
if (elem == NULL)
{
if ((elem = tsk_alloc (sizeof (ticker))) == NULL)
return NULL;
elem->flags = F_TEMP;
}
else
elem->flags = 0;
#else
if (elem == NULL)
return NULL;
#endif
elem->ticks = val;
C_ENTER;
elem->next = GLOBDATA ticker_chain;
GLOBDATA ticker_chain = elem;
C_LEAVE;
return elem;
}
/*
delete_ticker
unlink element from ticker chain.
*/
void far delete_ticker (tick_ptr elem)
{
tick_ptr curr;
CRITICAL;
curr = (tick_ptr)&GLOBDATA ticker_chain;
C_ENTER;
while (curr->next != NULL && curr->next != elem)
curr = curr->next;
curr->next = elem->next;
C_LEAVE;
#if (TSK_DYNAMIC)
if (elem->flags & F_TEMP)
tsk_free (elem);
#endif
}
/*
set_ticker
set new ticker value.
*/
void far set_ticker (tick_ptr elem, dword val)
{
CRITICAL;
C_ENTER;
elem->ticks = val;
C_LEAVE;
}
/*
get_ticker
get current ticker value.
*/
dword far get_ticker (tick_ptr elem)
{
dword tck;
CRITICAL;
C_ENTER;
tck = elem->ticks;
C_LEAVE;
return tck;
}