home *** CD-ROM | disk | FTP | other *** search
/ Aminet 10 / aminetcdnumber101996.iso / Aminet / util / misc / fifolib38_1.lha / fifo.c < prev    next >
C/C++ Source or Header  |  1995-12-20  |  11KB  |  484 lines

  1.  
  2. /*
  3.  *  FIFO.C
  4.  */
  5.  
  6. #include "defs.h"
  7.  
  8. #if !defined(__GNUC__)
  9. #define __inline__
  10. #endif
  11.  
  12. __inline__ static void SetEOF(Fifo *);
  13. void ReCalcReaderIdx(Fifo *);
  14. void FixFiFlags(Fifo *);
  15. LibCall void RequestFifo(FHan *, Message *, long);
  16. long AvailWBufSpace(Fifo *);
  17.  
  18. #if defined(__GNUC__)
  19. /*  Even works without optimization */
  20. #define BitTestSet(p,b) \
  21. ({ char __bittestset; \
  22.   __asm__ volatile ("bsetb %0,%2@; sne %0" \
  23.             : "=d" (__bittestset) : "0" (b), "a" (p) : "cc", "memory"); \
  24.   __bittestset; \
  25. })
  26. #else
  27. __stkargs
  28. short BitTestSet(char *, short);
  29. #endif
  30.  
  31. /*
  32.  *  Open up a new fifo by name.
  33.  */
  34.  
  35. LibCall FHan *
  36. OpenFifo(char *name, long bytes, long flags)
  37. {
  38.     Fifo *fifo;
  39.     FHan *fhan;
  40.  
  41.     {                /*  bytes a power of 2? */
  42.     unsigned long i = 8;
  43.     while (i) {
  44.         if (bytes == i)
  45.         break;
  46.         i = i << 1;
  47.     }
  48.     if (i == 0)
  49.         return(NULL);
  50.     }
  51.     if ((fhan = AllocMem(sizeof(FHan), MEMF_PUBLIC | MEMF_CLEAR))) {
  52.     Forbid();
  53.     if (NULL == (fifo = (Fifo *)FindName((MaxList *)&FifoList, name))) {
  54.         if ((fifo = AllocMem(sizeof(Fifo) - sizeof(fifo->fi_Buf) + bytes + strlen(name) + 1, MEMF_PUBLIC | MEMF_CLEAR))) {
  55.         AddTail((MaxList *)&FifoList, &fifo->fi_Node);
  56.         NewList((MaxList *)&fifo->fi_HanList);
  57.         NewList((MaxList *)&fifo->fi_WrNotify);
  58.         NewList((MaxList *)&fifo->fi_RdNotify);
  59.         fifo->fi_BufSize = bytes;
  60.         fifo->fi_BufMask = bytes - 1;
  61.         fifo->fi_Node.ln_Name = (char *)fifo + (sizeof(Fifo) - sizeof(fifo->fi_Buf)) + bytes;
  62.         strcpy(fifo->fi_Node.ln_Name, name);
  63.         InitSemaphore(&fifo->fi_SigSem);
  64.         }
  65.     }
  66.     if (fifo) {
  67.         AddTail((MaxList *)&fifo->fi_HanList, (MaxNode *)&fhan->fh_Node);
  68.         fhan->fh_Fifo = fifo;
  69.         fhan->fh_Flags = flags;
  70.  
  71.         fhan->fh_Msg.mn_ReplyPort = &fhan->fh_Port;
  72.  
  73.         fhan->fh_Port.mp_Node.ln_Type = NT_MSGPORT;
  74.         fhan->fh_Port.mp_Flags = PA_SIGNAL;
  75.         fhan->fh_Port.mp_SigBit = SIGB_SINGLE;
  76.  
  77.         NewList(&fhan->fh_Port.mp_MsgList);
  78.  
  79.         if (flags & FIFOF_READ) {
  80.         ++fifo->fi_RRefs;
  81.         fhan->fh_RIdx = fifo->fi_RIdx;
  82.         }
  83.         if (flags & FIFOF_WRITE) {
  84.         ++fifo->fi_WRefs;
  85.         }
  86.         /*
  87.         if (flags & FIFOF_CLOSEOF)
  88.         fifo->fi_Flags |= FIFOF_CLOSEOF;
  89.         */
  90.         if (flags & FIFOF_KEEPIFD)
  91.         fifo->fi_Flags |= FIFOF_KEEPIFD;
  92.         if (flags & FIFOF_RREQUIRED)
  93.         fifo->fi_Flags |= FIFOF_RREQUIRED;
  94.         ++fifo->fi_ORefs;
  95.         FixFiFlags(fifo);
  96.     } else {
  97.         FreeMem(fhan, sizeof(FHan));
  98.         fhan = NULL;
  99.     }
  100.     Permit();
  101.     }
  102.     return(fhan);
  103. }
  104.  
  105. /*
  106.  *  SetEOF()
  107.  */
  108.  
  109. __inline__ static
  110. void
  111. SetEOF(Fifo *fifo)
  112. {
  113.     FHan *fh;
  114.  
  115.     for (fh = (FHan *)fifo->fi_HanList.mlh_Head; fh->fh_Node.mln_Succ; fh = (FHan *)fh->fh_Node.mln_Succ)
  116.     fh->fh_Flags |= FIFOF_EOF;
  117.     fifo->fi_Flags |= FIFOF_EOF;
  118. }
  119.  
  120. /*
  121.  *  Close a previously opened fifo
  122.  */
  123.  
  124. LibCall void
  125. CloseFifo(FHan *fhan, long flags)
  126. {
  127.     Fifo *fifo;
  128.  
  129.     Forbid();
  130.     if ((fifo = fhan->fh_Fifo)) {
  131.     fhan->fh_Fifo = NULL;    /*  try to catch duplicate closes   */
  132.     Remove((MaxNode *)&fhan->fh_Node);
  133.  
  134.     --fifo->fi_ORefs;
  135.  
  136.     if (fhan->fh_Flags & FIFOF_WRITE) {
  137.         if (flags & FIFOF_EOF)
  138.         fifo->fi_Flags |= FIFOF_CLOSEOF;
  139.         if (--fifo->fi_WRefs == 0) {
  140.         if (fifo->fi_Flags & FIFOF_CLOSEOF) {
  141.             SetEOF(fifo);
  142.         }
  143.         }
  144.     }
  145.     if (fhan->fh_Flags & FIFOF_READ) {
  146.         --fifo->fi_RRefs;
  147.  
  148.         if (fifo->fi_RRefs == 0 && (fifo->fi_Flags & FIFOF_RREQUIRED)) {
  149.         Message *msg;
  150.         while ((msg = (Message *)RemHead((MaxList *)&fifo->fi_WrNotify)))
  151.             ReplyMsg(msg);
  152.         }
  153.     }
  154.  
  155.     if (fifo->fi_ORefs == 0) {
  156.         if ((fifo->fi_Flags & FIFOF_KEEPIFD) == 0 || fifo->fi_RIdx == fifo->fi_WIdx) {
  157.         Remove(&fifo->fi_Node);
  158.         FreeMem(fifo, sizeof(Fifo) - sizeof(fifo->fi_Buf) + fifo->fi_BufSize + strlen(fifo->fi_Node.ln_Name) + 1);
  159.         }
  160.     } else {
  161.         if (fhan->fh_Flags & FIFOF_WRITE && fifo->fi_Flags & FIFOF_EOF) {
  162.         Message *msg;
  163.  
  164.         /*  signal EOF to any blocked readers */
  165.         while ((msg = (Message *)RemHead((MaxList *)&fifo->fi_RdNotify)))
  166.             ReplyMsg(msg);
  167.         }
  168.         if (fhan->fh_Flags & FIFOF_READ)
  169.         ReCalcReaderIdx(fifo);
  170.         FixFiFlags(fifo);
  171.     }
  172.     FreeMem(fhan, sizeof(FHan));
  173.     }
  174.     Permit();
  175. }
  176.  
  177. /*
  178.  *  Read from a fifo.  Block if necessary (and not FIFOF_NBIO)
  179.  */
  180.  
  181. LibCall long
  182. ReadFifo(FHan *fhan, char **pbuf, long skip)
  183. {
  184.     long n;
  185.     Fifo *fifo = fhan->fh_Fifo;
  186.  
  187.     /*
  188.      *    attempt to clear <skip> bytes
  189.      */
  190.  
  191.     while (skip > 0) {
  192.     long widx = fifo->fi_WIdx;    /*  snapshot widx   */
  193.     long ridx = fhan->fh_RIdx;
  194.     long len;
  195.  
  196.     if (ridx <= widx) {
  197.         len = widx - ridx;
  198.     } else {
  199.         len = fifo->fi_BufSize - ridx;
  200.     }
  201.     if (len == 0)
  202.         break;
  203.     if (len > skip)
  204.         len = skip;
  205.     n += len;
  206.     skip -= len;
  207.  
  208.     Forbid();
  209.     fhan->fh_RIdx = (ridx + len) & fifo->fi_BufMask;
  210.     if (ridx == fifo->fi_RIdx)
  211.         ReCalcReaderIdx(fifo);      /*  update mast-idx/writer-waiters */
  212.     Permit();
  213.     }
  214.  
  215.     /*
  216.      *    return available data
  217.      */
  218.  
  219.     for (;;) {
  220.     long widx = fifo->fi_WIdx;    /*  snapshot widx   */
  221.     long ridx = fhan->fh_RIdx;
  222.  
  223.     if (ridx <= widx) {
  224.         n = widx - ridx;
  225.     } else {
  226.         n = fifo->fi_BufSize - ridx;
  227.     }
  228.     *pbuf = fifo->fi_Buf + ridx;
  229.     if (n == 0) {
  230.         /*
  231.          *    EOF on a per-handle basis since it gets cleared after EOF
  232.          *    is returned.
  233.          */
  234.  
  235.         if ((fhan->fh_Flags & FIFOF_EOF) || (fifo->fi_Flags & FIFOF_EOF)) {
  236.         /*fhan->fh_Flags &= ~FIFOF_EOF;*/
  237.         n = -1;
  238.         break;
  239.         }
  240.         if ((fhan->fh_Flags & FIFOF_NBIO) == 0) {
  241.         fhan->fh_Port.mp_SigTask = SysBase->ThisTask;
  242.         RequestFifo(fhan, &fhan->fh_Msg, FREQ_RPEND);
  243.         WaitPort(&fhan->fh_Port);
  244.         Remove((MaxNode *)&fhan->fh_Msg);
  245.         continue;
  246.         }
  247.     }
  248.     break;
  249.     }
  250.     return(n);
  251. }
  252.  
  253. /*
  254.  *  Write to a fifo
  255.  */
  256.  
  257. LibCall long
  258. WriteFifo(FHan *fhan, char *buf, long bytes)
  259. {
  260.     long n = -1;
  261.     short wsigchk = 0;
  262.     short normal  = 0;
  263.     Fifo *fifo = fhan->fh_Fifo;
  264.  
  265.     if (fifo->fi_RRefs == 0 && (fifo->fi_Flags & FIFOF_RREQUIRED))
  266.     return(-1);
  267.  
  268.     if (bytes < 0 || bytes > (fifo->fi_BufSize >> 1))
  269.     return(-2);
  270.  
  271.     Forbid();
  272.  
  273.     /*
  274.      *    A normal FIFO uses fi_SigSem
  275.      *    A non-normal FIFO cannot afford to block and uses fi_Lock
  276.      */
  277.  
  278.     if (fifo->fi_Flags & FIFOF_WRNORM)
  279.     normal = 1;
  280.  
  281.     if (normal) {
  282.     ObtainSemaphore(&fifo->fi_SigSem);
  283.     } else if (BitTestSet(&fifo->fi_Lock, 0) != 0) {
  284.     Permit();
  285.     return(n);
  286.     }
  287.  
  288.     {
  289.     n = 0;
  290.  
  291.     for (;;) {
  292.         while (bytes) {
  293.         long ridx = fifo->fi_RIdx;    /*  snapshot ridx   */
  294.         long len;
  295.  
  296.         if (fifo->fi_WIdx < ridx) {
  297.             len = ridx - fifo->fi_WIdx;
  298.             if (len <= bytes)           /*  FIFO FULL       */
  299.             break;
  300.         } else {
  301.             len = fifo->fi_BufSize - fifo->fi_WIdx;
  302.             if (len + ridx <= bytes)    /*  FIFO FULL       */
  303.             break;
  304.         }
  305.         if (len > bytes)
  306.             len = bytes;
  307.         CopyMem(buf, fifo->fi_Buf + fifo->fi_WIdx, len);
  308.         buf += len;
  309.         n += len;
  310.         bytes -= len;
  311.         fifo->fi_WIdx = (fifo->fi_WIdx + len) & fifo->fi_BufMask;
  312.         if (fhan->fh_Flags & FIFOF_NORMAL)
  313.             wsigchk = 1;
  314.         }
  315.  
  316.         /*
  317.          *    if fifo is full and NBIO not set, then block and loop
  318.          */
  319.         if (bytes && !(fhan->fh_Flags & FIFOF_NBIO)) {
  320.         fhan->fh_Port.mp_SigTask = SysBase->ThisTask;
  321.         RequestFifo(fhan, &fhan->fh_Msg, FREQ_WAVAIL);
  322.         WaitPort(&fhan->fh_Port);
  323.         Remove((MaxNode *)&fhan->fh_Msg);
  324.         continue;
  325.         }
  326.         break;
  327.     }
  328.  
  329.     /*
  330.      *  check for any blocked readers, data is probably now available
  331.      *  so wake them up.
  332.      */
  333.  
  334.     if (wsigchk && fifo->fi_RdNotify.mlh_Head->mln_Succ) {
  335.         Message *msg;
  336.  
  337.         while ((msg = (Message *)RemHead((MaxList *)&fifo->fi_RdNotify)))
  338.         ReplyMsg(msg);
  339.     }
  340.  
  341.     if (normal)
  342.         ReleaseSemaphore(&fifo->fi_SigSem);
  343.     else
  344.         fifo->fi_Lock = 0;
  345.     }
  346.     Permit();
  347.     return(n);
  348. }
  349.  
  350. LibCall long
  351. BufSizeFifo(FHan *fhan)
  352. {
  353.     return(fhan->fh_Fifo->fi_BufSize);
  354. }
  355.  
  356.  
  357. /*
  358.  *  Calculate distance between fh->fh_RIdx and fifo->fi_RIdx, shortest
  359.  *  wins.  If none found then nothing changes due to initial best set
  360.  *  to exactly the buffer size.
  361.  *
  362.  *  If the master index is modified and writers are waiting, signal them.
  363.  *
  364.  *  Called while Forbid() or otherwise reader-lockedout
  365.  */
  366.  
  367. void
  368. ReCalcReaderIdx(Fifo *fifo)
  369. {
  370.     FHan *fh;
  371.     long bestLen = fifo->fi_BufSize;
  372.     long ridx;
  373.  
  374.     for (fh = (FHan *)fifo->fi_HanList.mlh_Head; fh->fh_Node.mln_Succ; fh = (FHan *)fh->fh_Node.mln_Succ) {
  375.     if (fh->fh_Flags & FIFOF_READ) {
  376.         long len = (fh->fh_RIdx - fifo->fi_RIdx) & fifo->fi_BufMask;
  377.         if (len < bestLen)
  378.         bestLen = len;
  379.     }
  380.     }
  381.  
  382.     ridx = (fifo->fi_RIdx + bestLen) & fifo->fi_BufMask;
  383.  
  384.     /*
  385.      *    more buffer space available, wakeup writers?
  386.      */
  387.  
  388.     if (ridx != fifo->fi_RIdx) {
  389.     fifo->fi_RIdx = ridx;
  390.  
  391.     if (fifo->fi_WrNotify.mlh_Head->mln_Succ) {
  392.         Message *msg;
  393.  
  394.         if (AvailWBufSpace(fifo) >= (fifo->fi_BufSize >> 1)) {
  395.         while ((msg = (Message *)RemHead((MaxList *)&fifo->fi_WrNotify)))
  396.             ReplyMsg(msg);
  397.         }
  398.     }
  399.     }
  400. }
  401.  
  402. /*
  403.  *  FIXME, if state change occurs in master fifo, must unblock anybody
  404.  *  blocked due to previous information. XXX
  405.  */
  406.  
  407. void
  408. FixFiFlags(Fifo *fifo)
  409. {
  410.     FHan *fh;
  411.     long rflags = FIFOF_RDNORM | FIFOF_WRNORM;
  412.  
  413.     fifo->fi_Flags &= ~rflags;
  414.     for (fh = (FHan *)fifo->fi_HanList.mlh_Head; fh->fh_Node.mln_Succ; fh = (FHan *)fh->fh_Node.mln_Succ) {
  415.     if ((fh->fh_Flags & FIFOF_NORMAL) == 0) {
  416.         if (fh->fh_Flags & FIFOF_READ)
  417.         rflags &= ~FIFOF_RDNORM;
  418.         if (fh->fh_Flags & FIFOF_WRITE)
  419.         rflags &= ~FIFOF_WRNORM;
  420.     }
  421.     }
  422.     fifo->fi_Flags |= rflags;
  423. }
  424.  
  425. /*
  426.  *  request message on event.  Returns message immediately if event already
  427.  *  satisfied.
  428.  */
  429.  
  430. LibCall void
  431. RequestFifo(FHan *fhan, Message *msg, long req)
  432. {
  433.     Fifo *fifo = fhan->fh_Fifo;
  434.  
  435.     Forbid();
  436.  
  437.     switch(req) {
  438.     case FREQ_RPEND:
  439.     if ((fhan->fh_Flags & FIFOF_EOF) || fhan->fh_RIdx != fifo->fi_WIdx) {
  440.         ReplyMsg(msg);
  441.     } else {
  442.         msg->mn_Node.ln_Type = NT_MESSAGE;
  443.         AddTail((MaxList *)&fifo->fi_RdNotify, &msg->mn_Node);
  444.     }
  445.     break;
  446.     case FREQ_WAVAIL:
  447.     /*
  448.      *  determine available buffer space, alert if more than 1/2 empty.
  449.      *
  450.      *  check for broken pipe
  451.      */
  452.  
  453.     if (fifo->fi_RRefs == 0 && (fifo->fi_Flags & FIFOF_RREQUIRED)) {
  454.         ReplyMsg(msg);
  455.     } else if (AvailWBufSpace(fifo) >= (fifo->fi_BufSize >> 1)) {
  456.         ReplyMsg(msg);
  457.     } else {
  458.         msg->mn_Node.ln_Type = NT_MESSAGE;
  459.         AddTail((MaxList *)&fifo->fi_WrNotify, &msg->mn_Node);
  460.     }
  461.     break;
  462.     case FREQ_ABORT:
  463.     if (msg->mn_Node.ln_Type == NT_MESSAGE) {   /*  if not returned */
  464.         Remove(&msg->mn_Node);                  /*  return it       */
  465.         ReplyMsg(msg);
  466.     }
  467.     break;
  468.     }
  469.     Permit();
  470. }
  471.  
  472. long
  473. AvailWBufSpace(Fifo *fifo)
  474. {
  475.     long ridx = fifo->fi_RIdx;
  476.     long len;
  477.  
  478.     if (fifo->fi_WIdx < ridx)
  479.     len = ridx - fifo->fi_WIdx;
  480.     else
  481.     len = fifo->fi_BufSize - fifo->fi_WIdx + ridx - 1;
  482.     return(len);
  483. }
  484.