/***************************************************************************
 *                                                                         *
 *   TASKS.C                                                               *
 *                                                                         *
 *   Copyright (c) 1987-1997 Galacticomm, Inc.    All Rights Reserved.     *
 *                                                                         *
 *   These routines permit the use of "tasks", which are processes         *
 *   runnable within the server independent of any particular channel.     *
 *   Tasks are similar to rtkick()-based approaches to background          *
 *   processing, except that:                                              *
 *                                                                         *
 *        1) If the system is lightly loaded, the task(s) will consume     *
 *           most of the idle compute time;                                *
 *                                                                         *
 *        2) If the system is heavily loaded, the time devoted to task     *
 *           processing becomes "stretched out" so that real-time user     *
 *           performance is impacted as little as possible.                *
 *                                                                         *
 *                                            - T. Stryker 11/19/93        *
 *                                              C. Robert                  *
 *                                                                         *
 ***************************************************************************/

#include "gcomm.h"
#include "phasedbg.h"

/* don't know why, but VC++ gives redefinition warning on FILREV */
#undef FILREV
#define FILREV "$Revision: 5 $"

#define   TSKINC    5              /* increment size for expanding tsktbl  */

static INT tskhdl=-1;              /* handle of task function array        */

INT                                /*   returns taskid                     */
initask(                           /* start up a new task                  */
VOID (*tskaddr)(INT taskid))       /*   function address to invoke         */
{
     INT i,ntasks;
     VOID (**tsktbl)(INT taskid);

     if (tskhdl == -1) {
          tskhdl=newarr(TSKINC,sizeof(VOID (*)()));
     }
     ntasks=ninarr(tskhdl);
     tsktbl=arrbas(tskhdl);
     for (i=0 ; i < ntasks ; i++) {
          if (tsktbl[i] == NULL) {
               tsktbl[i]=tskaddr;
               return(i);
          }
     }
     add2arr(tskhdl,&tskaddr);
     return(ntasks);
}

VOID
mfytask(                           /* modify the function addr of a task   */
INT taskid,                        /*   taskid to modify                   */
VOID (*tskaddr)(INT taskid))       /*   new function address to invoke     */
{
     if (taskid >= ninarr(tskhdl) || taskid < 0) {
          catastro("MFYTASK: BAD TASKID %d",taskid);
     }
     *(VOID (**)())arrelem(tskhdl,taskid)=tskaddr;
}

VOID
prctask(VOID)                      /* process the next task (round robin)  */
{
     INT brobin,ntasks;
     VOID (**tsktbl)(INT taskid);
     static INT rrobin=0;

     if (tskhdl == -1) {
          tskhdl=newarr(TSKINC,sizeof(VOID (*)()));
     }
     if ((ntasks=ninarr(tskhdl)) != 0) {
          tsktbl=arrbas(tskhdl);
          brobin=rrobin;
          do {
               rrobin=(rrobin+1)%ntasks;
               if (tsktbl[rrobin] != NULL) {
                    BEG_PHASE("Task",tsktbl[rrobin]);
                    (*tsktbl[rrobin])(rrobin);
                    END_PHASE("Task",rrobin);
                    break;
               }
          } while (rrobin != brobin);
     }
}
