home *** CD-ROM | disk | FTP | other *** search
/ Frostbyte's 1980s DOS Shareware Collection / floppyshareware.zip / floppyshareware / DOOG / CTASK.ZIP / TSKTIMER.C < prev    next >
C/C++ Source or Header  |  1989-12-21  |  17KB  |  712 lines

  1. /*
  2.     --- Version 2.0 89-12-15 14:57 ---
  3.  
  4.    TSKTIM.C - CTask - Timer routines.
  5.  
  6.    CTask - a Multitasking Kernel for C
  7.  
  8.    Public Domain Software written by
  9.       Thomas Wagner
  10.       Patschkauer Weg 31
  11.       D-1000 Berlin 33
  12.       West Germany
  13.  
  14.    No rights reserved.
  15.  
  16.    This file is new with 1.2. The timer related functions were moved
  17.    from tskmain to this module.
  18.  
  19.    Timer logic has been significantly changed in version 2.0.
  20. */
  21.  
  22. #include <stdio.h>
  23.  
  24. #include "tsk.h"
  25. #include "tsklocal.h"
  26.  
  27. #include <stdarg.h>
  28.  
  29. /*
  30.    tsk_timer_action 
  31.       performs the necessary action when a timeout occurred.
  32.       Starting with version 2.0, this part is not critical,
  33.       and may be preempted.
  34. */
  35.  
  36. local void near tsk_timer_action (tlinkptr elem)
  37. {
  38.    tcbptr task;
  39.    byte st;
  40.    CRITICAL;
  41.  
  42.    switch (elem->link.kind)
  43.       {
  44.       /* Note: TKIND_WAKE handled in task body */
  45.  
  46.       case TKIND_TASK:  task = (tcbptr) elem->strucp;
  47.                         C_ENTER;
  48.                         st = task->state;
  49.  
  50.                         if (st == ST_WAITING || st == ST_DELAYED)
  51.                            {
  52.                            task->retptr = 
  53.                               ((elem->elkind & 0xf0) == TELEM_TIMER)
  54.                                  ? TTIMEOUT
  55.                                  : TWATCH;
  56.                            tsk_runable (task);
  57.                            }
  58.                         C_LEAVE;
  59.                         break;
  60.  
  61.       case TKIND_FLAG:  set_flag ((flagptr) elem->strucp);
  62.                         break;
  63.  
  64.       case TKIND_COUNTER: inc_counter ((counterptr) elem->strucp);
  65.                         break;
  66.  
  67.       case TKIND_PROC:  ((funcptr_void_fp) elem->strucp)(elem);
  68.                         break;
  69.  
  70.       default:          break;
  71.       }
  72. }
  73.  
  74.  
  75. /*
  76.    tsk_exec_watch 
  77.       checks the watch condition, and returns 1 if the 
  78.       condition is met.
  79. */
  80.  
  81. local int near tsk_exec_watch (tlinkptr curr)
  82. {
  83.    word val, cmp;
  84.    int elcmp;
  85.  
  86.    elcmp = curr->elkind & 0x0f;
  87.  
  88.    switch (curr->elkind & 0xf0)
  89.       {
  90.       case TELEM_MEM:   val = *(curr->elem.mem.address) & curr->elem.mem.mask;
  91.                         cmp = curr->elem.mem.compare;
  92.                         if (elcmp == TCMP_CHG)
  93.                            curr->elem.mem.compare = val;
  94.                         break;
  95.  
  96.       /* Microsoft C generates "Internal Compiler Error" on compiling
  97.          the following statement */
  98. #if (TURBO)
  99.       case TELEM_PORT:  val = (curr->elem.port.in_word)
  100.                               ? tsk_inpw (curr->elem.port.port)
  101.                               : (word)tsk_inp (curr->elem.port.port);
  102. #else
  103.       case TELEM_PORT:  if (curr->elem.port.in_word)
  104.                            val = tsk_inpw (curr->elem.port.port);
  105.                         else
  106.                            val = (word)tsk_inp (curr->elem.port.port);
  107. #endif
  108.                         val &= curr->elem.port.mask;
  109.                         cmp = curr->elem.port.compare;
  110.                         if (elcmp == TCMP_CHG)
  111.                            curr->elem.port.compare = val;
  112.                         break;
  113.  
  114.       default:          return 0;
  115.       }
  116.  
  117.    switch (elcmp)
  118.       {
  119.       case TCMP_EQ:  return val == cmp;
  120.       case TCMP_CHG:
  121.       case TCMP_NE:  return val != cmp;
  122.       case TCMP_LE:  return val <= cmp;
  123.       case TCMP_GE:  return val >= cmp;
  124.       case TCMP_LES: return (int)val <= (int)cmp;
  125.       case TCMP_GES: return (int)val >= (int)cmp;
  126.       default:       return 0;
  127.       }
  128. }
  129.  
  130.  
  131. /*
  132.    The timer task handles all timeouts.
  133.  
  134.    Starting with version 2.0, two queues are maintained, one for
  135.    the timeouts, and one for watch elements.
  136.  
  137.    The timeout queue now is ordered, with the tick count in the queue
  138.    element head holding the difference in ticks to the previous element.
  139.    Thus, only the first element of the timeout queue has to be counted
  140.    down, which will considerably speed up processing here if there are
  141.    multiple elements in the queue.
  142.  
  143.    The watch queue is unordered.
  144.  
  145.    The loop to check the queue elements is fully protected.
  146.    This allows other tasks access to the timer queue. The
  147.    concept used in pre-2.0 versions was pretty complicated to
  148.    handle, and still had to run with interupts disabled for
  149.    most parts.
  150.  
  151.    The new logic unchains expired timeout elements immediately, within
  152.    the protected loop, but delays processing them (except for task waits)
  153.    until after the loop is finished. Modification of those elements should
  154.    not normally occur, and will be rejected. The processing of the
  155.    timeout/watch action can thus be handled with interrupts enabled.
  156. */
  157.  
  158. void far tsk_timer (void)
  159. {
  160.    queptr curr;
  161.    queptr help;
  162.    queptr tlast;
  163.    byte state;
  164.    CRITICAL;
  165.  
  166.    while (1)
  167.       {
  168.       wait_counter_set (&tsk_timer_counter, 0L);
  169.  
  170.       tlast = NULL;
  171.  
  172.       C_ENTER;
  173.  
  174.       /* First, check the timeout queue. In version 2.0, only
  175.          the first element has to be counted down.
  176.       */
  177.  
  178.       if ((curr = GLOBDATA timer_queue.first)->kind)
  179.          if (!--curr->el.ticks)
  180.             {
  181.             /* Remove all counted-down elements from the timer queue,
  182.                marking them as not in queue and busy, and chaining them
  183.                through the "prev" pointer.
  184.                We can then process the elements outside the critical 
  185.                section.
  186.                The only hairy situation could arise if a task is
  187.                waiting for an event with timeout, the timeout
  188.                hits, we take it off the queue, are preempted, and
  189.                the task is again made waiting. This would kill our
  190.                chain. So this special case is handled here.
  191.             */
  192.  
  193.             do
  194.                {
  195.                help = curr;
  196.                curr = curr->next;
  197.                tsk_dequeue (help);
  198.                if (help->kind == TKIND_WAKE)
  199.                   {
  200.                   ((tcbptr)((tlinkptr)help)->strucp)->retptr = TTIMEOUT;
  201.                   tsk_runable (((tlinkptr)help)->strucp);
  202.                   }
  203.                else
  204.                   {
  205.                   ((tlinkptr)help)->flags |= TFLAG_BUSY;
  206.                   help->prev = tlast;
  207.                   tlast = help;
  208.                   }
  209.                }
  210.             while (curr->kind && !curr->el.ticks);
  211.             }
  212.  
  213.       /* Now, check the watch queue. */
  214.  
  215.       for (curr = GLOBDATA watch_queue.first; curr->kind; )
  216.          {
  217.          help = curr;
  218.          curr = curr->next;
  219.  
  220.          if (tsk_exec_watch ((tlinkptr)help))
  221.             {
  222.             if (help->kind == TKIND_WAKE)
  223.                {
  224.                ((tcbptr)((tlinkptr)help)->strucp)->retptr = TWATCH;
  225.                tsk_runable (((tlinkptr)help)->strucp);
  226.                }
  227.             else
  228.                {
  229.                tsk_dequeue (help);
  230.                ((tlinkptr)help)->flags |= TFLAG_BUSY;
  231.                help->prev = tlast;
  232.                tlast = help;
  233.                }
  234.             }
  235.          }
  236.  
  237.       /* Ready checking the queues, we can now re-enable interrupts
  238.          for execution of the timeout/watch action. Interrupts are
  239.          disabled only for a short period to check the state and
  240.          re-enqueue repeat elements.  */
  241.  
  242.       C_LEAVE;
  243.  
  244.       while (tlast != NULL)
  245.          {
  246.          curr = tlast;
  247.          tlast = tlast->prev;
  248.          tsk_timer_action ((tlinkptr)curr);
  249.  
  250.          C_ENTER;
  251.          state = ((tlinkptr)curr)->tstate;
  252.          if (state == TSTAT_REPEAT)
  253.             tsk_enqtimer (curr, ((tlinkptr)curr)->elem.time.reload);
  254.          else if (state == TSTAT_CONTWATCH)
  255.             tsk_putqueue (&GLOBDATA watch_queue, curr);
  256.          else
  257.             state = ((tlinkptr)curr)->tstate = TSTAT_IDLE;
  258.  
  259.          ((tlinkptr)curr)->flags &= ~TFLAG_BUSY;
  260.          C_LEAVE;
  261.  
  262. #if (TSK_DYNAMIC)
  263.          if (state == TSTAT_IDLE && ((tlinkptr)curr)->flags & TFLAG_TEMP)
  264.             tsk_free (curr);
  265. #endif
  266.          }
  267.       }
  268. }
  269.  
  270. /*
  271.    int8 is the timer interrupt chain task.
  272. */
  273.  
  274. #if (IBM)
  275.  
  276. void far tsk_int8 (void)
  277. {
  278.    while (1)
  279.       {
  280.       wait_counter_set (&tsk_int8_counter, 0L);
  281.       tsk_chain_timer ();
  282.       }
  283. }
  284.  
  285. #endif
  286.  
  287. /* ---------------------------------------------------------------------- */
  288.  
  289.  
  290. /*
  291.    t_delay
  292.       delay the current task by "ticks" clock ticks.
  293.       If ticks is zero, the task is stopped.
  294. */
  295.  
  296. int far t_delay (dword ticks)
  297. {
  298.    tcbptr task = GLOBDATA current_task;
  299.  
  300.    tsk_cli ();
  301.    tsk_dequeue (&task->cqueue);
  302.    tsk_deqtimer (&task->timerq.link);
  303.    task->qhead = NULL;
  304.    if (ticks)
  305.       {
  306.       task->state = ST_DELAYED;
  307.       task->timerq.link.kind = TKIND_WAKE;
  308.       task->timerq.tstate = TSTAT_COUNTDOWN;
  309.       task->timerq.elkind = TELEM_TIMER;
  310.       tsk_enqtimer (&task->timerq.link, ticks);
  311.       }
  312.    else
  313.       task->state = ST_STOPPED;
  314.  
  315.    schedule ();
  316.    return (int)task->retptr;
  317. }
  318.  
  319.  
  320. /*
  321.    setup_telem
  322.       Initializes a timeout/watch element.
  323. */
  324.  
  325. local tlinkptr near tsk_setup_telem (tlinkptr elem, farptr strucp,
  326.                                      byte kind, dword upar)
  327. {
  328.    if (kind <= TKIND_TASK || kind > TKIND_COUNTER)
  329.       return NULL;
  330.  
  331. #if (TSK_DYNAMIC)
  332.    if (elem == NULL)
  333.       {
  334.       if ((elem = tsk_alloc (sizeof (tlink))) == NULL)
  335.          return NULL;
  336.       elem->flags = TFLAG_TEMP;
  337.       }
  338.    else
  339.       elem->flags = 0;
  340. #else
  341.    elem->flags = 0;
  342. #endif
  343.  
  344.    elem->link.kind = kind;
  345.    elem->strucp = strucp;
  346.    elem->user_parm = upar;
  347.    return elem;
  348. }
  349.  
  350.  
  351. /*
  352.    create_timer
  353.       Creates a timer queue timeout element. The element is inserted into
  354.       the timeout queue.
  355. */
  356.  
  357. tlinkptr far create_timer (tlinkptr elem, dword tout, farptr strucp,
  358.                            byte kind, byte rept, ...)
  359. {
  360.    va_list val;
  361.    CRITICAL;
  362.  
  363.    va_start (val, rept);
  364.    elem = tsk_setup_telem (elem, strucp, kind, va_arg (val, dword));
  365.    if (elem == NULL)
  366.       return NULL;
  367.  
  368.    elem->tstate = (byte)((rept) ? TSTAT_REPEAT : TSTAT_COUNTDOWN);
  369.    elem->elkind = TELEM_TIMER;
  370.    tout = elem->elem.time.reload = tsk_timeout(tout);
  371.  
  372.    if (tout)
  373.       {
  374.       C_ENTER;
  375.       tsk_enqtimer (&elem->link, tout);
  376.       C_LEAVE;
  377.       }
  378.  
  379.    va_end (val);
  380.    return elem;
  381. }
  382.  
  383.  
  384. /*
  385.    delete_timer
  386.       Deletes a timeout element.
  387. */
  388.  
  389. void far delete_timer (tlinkptr elem)
  390. {
  391.    CRITICAL;
  392.  
  393.    C_ENTER;
  394.    if (elem->flags & TFLAG_BUSY)
  395.       {
  396.       elem->tstate = (byte)TSTAT_REMOVE;
  397.       C_LEAVE;
  398.       return;
  399.       }
  400.  
  401.    tsk_deqtimer (&elem->link);
  402.    C_LEAVE;
  403.  
  404. #if (TSK_DYNAMIC)
  405.    if (elem->flags & TFLAG_TEMP)
  406.       tsk_free (elem);
  407. #endif
  408. }
  409.  
  410.  
  411. /*
  412.    change_timer
  413.       Changes the timeout and/or repeat-flag in a timer element.
  414.       If the timer was idle, it is inserted into the timeout queue.
  415.  
  416.       If 0 is passed as timeout, the element is removed from the
  417.       timeout queue (same as delete_timer).
  418.  
  419.       This routine can *not* be used for dynamically allocated
  420.       timer elements.
  421. */
  422.  
  423. void far change_timer (tlinkptr elem, dword tout, byte rept, ...)
  424. {
  425.    va_list val;
  426.    CRITICAL;
  427.  
  428.    if (!tout)
  429.       {
  430.       delete_timer (elem);
  431.       return;
  432.       }
  433.  
  434.    if (elem->flags & TFLAG_TEMP)
  435.       return;
  436.  
  437.    va_start (val, rept);
  438.    C_ENTER;
  439.    elem->user_parm = va_arg (val, dword);
  440.    tout = elem->elem.time.reload = tsk_timeout(tout);
  441.    elem->tstate = (byte)((rept) ? TSTAT_REPEAT : TSTAT_COUNTDOWN);
  442.  
  443.    if (!(elem->flags & TFLAG_BUSY))
  444.       {
  445.       tsk_deqtimer (&elem->link);
  446.       if (tout)
  447.          tsk_enqtimer (&elem->link, tout);
  448.       }
  449.    C_LEAVE;
  450.    va_end (val);
  451. }
  452.  
  453.  
  454. /*
  455.    create_memory_watch
  456.       Creates a timer queue memory watch element. The element is 
  457.       inserted into the watch queue.
  458. */
  459.  
  460. tlinkptr far create_memory_watch (tlinkptr elem, farptr address, 
  461.                                   word mask, word compare, byte cmpkind,
  462.                                   farptr strucp, byte kind, byte rept, ...)
  463. {
  464.    va_list val;
  465.    CRITICAL;
  466.  
  467.    if (cmpkind < TCMP_EQ || cmpkind > TCMP_CHG)
  468.       return NULL;
  469.  
  470.    va_start (val, rept);
  471.    elem = tsk_setup_telem (elem, strucp, kind, va_arg (val, dword));
  472.    if (elem == NULL)
  473.       return NULL;
  474.  
  475.    elem->elkind = (byte)(TELEM_MEM | cmpkind);
  476.    elem->tstate = (byte)((rept) ? TSTAT_CONTWATCH : TSTAT_WATCH);
  477.    elem->elem.mem.address = address;
  478.    elem->elem.mem.mask = mask;
  479.    elem->elem.mem.compare = compare;
  480.  
  481.    C_ENTER;
  482.    tsk_putqueue (&GLOBDATA watch_queue, &elem->link);
  483.    C_LEAVE;
  484.  
  485.    va_end (val);
  486.    return elem;
  487. }
  488.  
  489.  
  490. /*
  491.    create_port_watch
  492.       Creates a timer queue port watch element. The element is 
  493.       inserted into the timeout queue.
  494. */
  495.  
  496. tlinkptr far create_port_watch (tlinkptr elem, word port, byte in_word,
  497.                                 word mask, word compare, byte cmpkind,
  498.                                 farptr strucp, byte kind, byte rept, ...)
  499. {
  500.    va_list val;
  501.    CRITICAL;
  502.  
  503.    if (cmpkind < TCMP_EQ || cmpkind > TCMP_CHG)
  504.       return NULL;
  505.  
  506.    va_start (val, rept);
  507.    elem = tsk_setup_telem (elem, strucp, kind, va_arg (val, dword));
  508.    if (elem == NULL)
  509.       return NULL;
  510.  
  511.    elem->elkind = (byte)(TELEM_PORT | cmpkind);
  512.    elem->tstate = (byte)((rept) ? TSTAT_CONTWATCH : TSTAT_WATCH);
  513.    elem->elem.port.port = port;
  514.    elem->elem.port.in_word = in_word;
  515.    elem->elem.port.mask = mask;
  516.    elem->elem.port.compare = compare;
  517.  
  518.    C_ENTER;
  519.    tsk_putqueue (&GLOBDATA watch_queue, &elem->link);
  520.    C_LEAVE;
  521.  
  522.    va_end (val);
  523.    return elem;
  524. }
  525.  
  526.  
  527. /*
  528.    wait_port
  529.       Delay current task until specified port watch condition is met.
  530. */
  531.  
  532. int far wait_port (word port, byte in_word,
  533.                    word mask, word compare, byte cmpkind)
  534. {
  535.    tlinkptr elem;
  536.    tcbptr task = GLOBDATA current_task;
  537.  
  538.    if (cmpkind < TCMP_EQ || cmpkind > TCMP_CHG)
  539.       return 1;
  540.  
  541.    elem = &task->timerq;
  542.    elem->link.kind = TKIND_WAKE;
  543.    elem->elkind = (byte)(TELEM_PORT | cmpkind);
  544.    elem->tstate = (byte)(TSTAT_WATCH);
  545.    elem->elem.port.port = port;
  546.    elem->elem.port.in_word = in_word;
  547.    elem->elem.port.mask = mask;
  548.    elem->elem.port.compare = compare;
  549.  
  550.    tsk_cli ();
  551.    task->state = ST_DELAYED;
  552.    task->qhead = NULL;
  553.  
  554.    tsk_putqueue (&GLOBDATA watch_queue, &elem->link);
  555.  
  556.    schedule ();
  557.    return (int)task->retptr;
  558. }
  559.  
  560.  
  561. /*
  562.    wait_memory
  563.       Delay current task until specified memory watch condition is met.
  564. */
  565.  
  566. int far wait_memory (farptr address, 
  567.                      word mask, word compare, byte cmpkind)
  568. {
  569.    tlinkptr elem;
  570.    tcbptr task = GLOBDATA current_task;
  571.  
  572.    if (cmpkind < TCMP_EQ || cmpkind > TCMP_CHG)
  573.       return 1;
  574.  
  575.    elem = &task->timerq;
  576.    elem->link.kind = TKIND_WAKE;
  577.    elem->elkind = (byte)(TELEM_MEM | cmpkind);
  578.    elem->tstate = (byte)(TSTAT_WATCH);
  579.    elem->elem.mem.address = address;
  580.    elem->elem.mem.mask = mask;
  581.    elem->elem.mem.compare = compare;
  582.  
  583.    tsk_cli ();
  584.    task->state = ST_DELAYED;
  585.    task->qhead = NULL;
  586.  
  587.    tsk_putqueue (&GLOBDATA watch_queue, &elem->link);
  588.  
  589.    schedule ();
  590.    return (int)task->retptr;
  591. }
  592.  
  593.  
  594. /*
  595.    delete_watch
  596.       Deletes a watch element.
  597. */
  598.  
  599. void far delete_watch (tlinkptr elem)
  600. {
  601.    CRITICAL;
  602.  
  603.    C_ENTER;
  604.    if (elem->flags & TFLAG_BUSY)
  605.       {
  606.       elem->tstate = (byte)TSTAT_REMOVE;
  607.       C_LEAVE;
  608.       return;
  609.       }
  610.  
  611.    tsk_dequeue (&elem->link);
  612.    C_LEAVE;
  613.  
  614. #if (TSK_DYNAMIC)
  615.    if (elem->flags & TFLAG_TEMP)
  616.       tsk_free (elem);
  617. #endif
  618. }
  619.  
  620.  
  621. /*
  622.    create_ticker
  623.       link element into ticker chain.
  624. */
  625.  
  626. tick_ptr far create_ticker (tick_ptr elem, dword val)
  627. {
  628.    CRITICAL;
  629.  
  630. #if (TSK_DYNAMIC)
  631.    if (elem == NULL)
  632.       {
  633.       if ((elem = tsk_alloc (sizeof (ticker))) == NULL)
  634.          return NULL;
  635.       elem->flags = F_TEMP;
  636.       }
  637.    else
  638.       elem->flags = 0;
  639. #else
  640.    if (elem == NULL)
  641.       return NULL;
  642. #endif
  643.  
  644.    elem->ticks = val;
  645.    C_ENTER;
  646.    elem->next = GLOBDATA ticker_chain;
  647.    GLOBDATA ticker_chain = elem;
  648.    C_LEAVE;
  649.  
  650.    return elem;
  651. }
  652.  
  653.  
  654. /*
  655.    delete_ticker
  656.       unlink element from ticker chain.
  657. */
  658.  
  659. void far delete_ticker (tick_ptr elem)
  660. {
  661.    tick_ptr curr;
  662.    CRITICAL;
  663.  
  664.    curr = (tick_ptr)&GLOBDATA ticker_chain;
  665.  
  666.    C_ENTER;
  667.    while (curr->next != NULL && curr->next != elem)
  668.       curr = curr->next;
  669.  
  670.    curr->next = elem->next;
  671.    C_LEAVE;
  672. #if (TSK_DYNAMIC)
  673.    if (elem->flags & F_TEMP)
  674.       tsk_free (elem);
  675. #endif
  676. }
  677.  
  678. /*
  679.    set_ticker
  680.       set new ticker value.
  681. */
  682.  
  683. void far set_ticker (tick_ptr elem, dword val)
  684. {
  685.    CRITICAL;
  686.  
  687.    C_ENTER;
  688.    elem->ticks = val;
  689.    C_LEAVE;
  690. }
  691.  
  692.  
  693. /*
  694.    get_ticker
  695.       get current ticker value.
  696. */
  697.  
  698. dword far get_ticker (tick_ptr elem)
  699. {
  700.    dword tck;
  701.    CRITICAL;
  702.  
  703.    C_ENTER;
  704.    tck = elem->ticks;
  705.    C_LEAVE;
  706.    return tck;
  707. }
  708.  
  709.  
  710.  
  711.  
  712.