home *** CD-ROM | disk | FTP | other *** search
- /*
- --- 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;
- }
-
-
-
-
-