home *** CD-ROM | disk | FTP | other *** search
/ World of A1200 / World_Of_A1200.iso / programs / communic / inetutils / contrib / amipop18.lha / AmiPOP18 / source / asyncio.c next >
C/C++ Source or Header  |  1994-02-28  |  21KB  |  639 lines

  1. #include "pop.h"
  2.  
  3.  
  4. /*****************************************************************************/
  5.  
  6.  
  7. extern struct DosLibrary *DOSBase;
  8. extern struct ExecBase *SysBase;
  9.  
  10.  
  11. /*****************************************************************************/
  12.  
  13.  
  14. /* this macro lets us long-align structures on the stack */
  15. #define D_S(type,name) char a_##name[sizeof(type)+3]; \
  16.                type *name = (type *)((LONG)(a_##name+3) & ~3);
  17.  
  18.  
  19. /*****************************************************************************/
  20.  
  21.  
  22. /* send out an async packet to the file system. */
  23. static void SendPacket(struct AsyncFile *file, APTR arg2)
  24. {
  25.     file->af_Packet.sp_Pkt.dp_Port = &file->af_PacketPort;
  26.     file->af_Packet.sp_Pkt.dp_Arg2 = (LONG)arg2;
  27.     PutMsg(file->af_Handler, &file->af_Packet.sp_Msg);
  28.     file->af_PacketPending = TRUE;
  29. }
  30.  
  31.  
  32. /*****************************************************************************/
  33.  
  34.  
  35. /* this function waits for a packet to come back from the file system. If no
  36.  * packet is pending, state from the previous packet is returned. This ensures
  37.  * that once an error occurs, it state is maintained for the rest of the life
  38.  * of the file handle.
  39.  *
  40.  * This function also deals with IO errors, bringing up the needed DOS
  41.  * requesters to let the user retry an operation or cancel it.
  42.  */
  43. static LONG WaitPacket(struct AsyncFile *file)
  44. {
  45. LONG bytes;
  46.  
  47.     if (file->af_PacketPending)
  48.     {
  49.         while (TRUE)
  50.         {
  51.             /* This enables signalling when a packet comes back to the port */
  52.             file->af_PacketPort.mp_Flags = PA_SIGNAL;
  53.  
  54.             /* Wait for the packet to come back, and remove it from the message
  55.              * list. Since we know no other packets can come in to the port, we can
  56.              * safely use Remove() instead of GetMsg(). If other packets could come in,
  57.              * we would have to use GetMsg(), which correctly arbitrates access in such
  58.              * a case
  59.              */
  60.             Remove((struct Node *)WaitPort(&file->af_PacketPort));
  61.  
  62.             /* set the port type back to PA_IGNORE so we won't be bothered with
  63.              * spurious signals
  64.              */
  65.             file->af_PacketPort.mp_Flags = PA_IGNORE;
  66.  
  67.             /* mark packet as no longer pending since we removed it */
  68.             file->af_PacketPending = FALSE;
  69.  
  70.             bytes = file->af_Packet.sp_Pkt.dp_Res1;
  71.             if (bytes >= 0)
  72.             {
  73.                 /* packet didn't report an error, so bye... */
  74.                 return(bytes);
  75.             }
  76.  
  77.             /* see if the user wants to try again... */
  78.             if (ErrorReport(file->af_Packet.sp_Pkt.dp_Res2,REPORT_STREAM,file->af_File,NULL))
  79.                 return(-1);
  80.  
  81.             /* user wants to try again, resend the packet */
  82.             if (file->af_ReadMode)
  83.                 SendPacket(file,file->af_Buffers[file->af_CurrentBuf]);
  84.             else
  85.                 SendPacket(file,file->af_Buffers[1 - file->af_CurrentBuf]);
  86.         }
  87.     }
  88.  
  89.     /* last packet's error code, or 0 if packet was never sent */
  90.     SetIoErr(file->af_Packet.sp_Pkt.dp_Res2);
  91.  
  92.     return(file->af_Packet.sp_Pkt.dp_Res1);
  93. }
  94.  
  95.  
  96. /*****************************************************************************/
  97.  
  98.  
  99. /* this function puts the packet back on the message list of our
  100.  * message port.
  101.  */
  102. static void RequeuePacket(struct AsyncFile *file)
  103. {
  104.     AddHead(&file->af_PacketPort.mp_MsgList,&file->af_Packet.sp_Msg.mn_Node);
  105.     file->af_PacketPending = TRUE;
  106. }
  107.  
  108.  
  109. /*****************************************************************************/
  110.  
  111.  
  112. /* this function records a failure from a synchronous DOS call into the
  113.  * packet so that it gets picked up by the other IO routines in this module
  114.  */
  115. static void RecordSyncFailure(struct AsyncFile *file)
  116. {
  117.     file->af_Packet.sp_Pkt.dp_Res1 = -1;
  118.     file->af_Packet.sp_Pkt.dp_Res2 = IoErr();
  119. }
  120.  
  121.  
  122. /*****************************************************************************/
  123.  
  124.  
  125. struct AsyncFile *OpenAsync(const STRPTR fileName, UBYTE accessMode, LONG bufferSize)
  126. {
  127. struct AsyncFile  *file;
  128. struct FileHandle *fh;
  129. BPTR               handle;
  130. BPTR               lock;
  131. LONG               blockSize;
  132. D_S(struct InfoData,infoData);
  133.  
  134.     handle = NULL;
  135.     file   = NULL;
  136.     lock   = NULL;
  137.  
  138.     if (accessMode == MODE_READ)
  139.     {
  140.         if (handle = Open(fileName,MODE_OLDFILE))
  141.             lock = Lock(fileName,ACCESS_READ);
  142.     }
  143.     else
  144.     {
  145.         if (accessMode == MODE_WRITE)
  146.         {
  147.             handle = Open(fileName,MODE_NEWFILE);
  148.         }
  149.         else if (accessMode == MODE_APPEND)
  150.         {
  151.             /* in append mode, we open for writing, and then seek to the
  152.              * end of the file. That way, the initial write will happen at
  153.              * the end of the file, thus extending it
  154.              */
  155.  
  156.             if (handle = Open(fileName,MODE_READWRITE))
  157.             {
  158.                 if (Seek(handle,0,OFFSET_END) < 0)
  159.                 {
  160.                     Close(handle);
  161.                     handle = NULL;
  162.                 }
  163.             }
  164.         }
  165.  
  166.         /* we want a lock on the same device as where the file is. We can't
  167.          * use DupLockFromFH() for a write-mode file though. So we get sneaky
  168.          * and get a lock on the parent of the file
  169.          */
  170.         if (handle)
  171.             lock = ParentOfFH(handle);
  172.     }
  173.  
  174.     if (handle)
  175.     {
  176.         /* if it was possible to obtain a lock on the same device as the
  177.          * file we're working on, get the block size of that device and
  178.          * round up our buffer size to be a multiple of the block size.
  179.          * This maximizes DMA efficiency.
  180.          */
  181.  
  182.         blockSize = 512;
  183.         if (lock)
  184.         {
  185.             if (Info(lock,infoData))
  186.             {
  187.                 blockSize = infoData->id_BytesPerBlock;
  188.                 bufferSize = (((bufferSize + blockSize - 1) / blockSize) * blockSize) * 2;
  189.             }
  190.             UnLock(lock);
  191.         }
  192.  
  193.         /* now allocate the ASyncFile structure, as well as the read buffers.
  194.          * Add 15 bytes to the total size in order to allow for later
  195.          * quad-longword alignement of the buffers
  196.          */
  197.  
  198.         if (file = AllocVec(sizeof(struct AsyncFile) + bufferSize + 15,MEMF_ANY))
  199.         {
  200.             file->af_File      = handle;
  201.             file->af_ReadMode  = (accessMode == MODE_READ);
  202.             file->af_BlockSize = blockSize;
  203.  
  204.             /* initialize the ASyncFile structure. We do as much as we can here,
  205.              * in order to avoid doing it in more critical sections
  206.              *
  207.              * Note how the two buffers used are quad-longword aligned. This
  208.          * helps performance on 68040 systems with copyback cache. Aligning
  209.              * the data avoids a nasty side-effect of the 040 caches on DMA.
  210.              * Not aligning the data causes the device driver to have to do
  211.              * some magic to avoid the cache problem. This magic will generally
  212.              * involve flushing the CPU caches. This is very costly on an 040.
  213.              * Aligning things avoids the need for magic, at the cost of at
  214.              * most 15 bytes of ram.
  215.              */
  216.  
  217.             fh                     = BADDR(file->af_File);
  218.             file->af_Handler       = fh->fh_Type;
  219.             file->af_BufferSize    = bufferSize / 2;
  220.             file->af_Buffers[0]    = (APTR)(((ULONG)file + sizeof(struct AsyncFile) + 15) & 0xfffffff0);
  221.             file->af_Buffers[1]    = (APTR)((ULONG)file->af_Buffers[0] + file->af_BufferSize);
  222.             file->af_Offset        = file->af_Buffers[0];
  223.             file->af_CurrentBuf    = 0;
  224.             file->af_SeekOffset    = 0;
  225.             file->af_PacketPending = FALSE;
  226.  
  227.             /* this is the port used to get the packets we send out back.
  228.              * It is initialized to PA_IGNORE, which means that no signal is
  229.              * generated when a message comes in to the port. The signal bit
  230.              * number is initialized to SIGB_SINGLE, which is the special bit
  231.              * that can be used for one-shot signalling. The signal will never
  232.              * be set, since the port is of type PA_IGNORE. We'll change the
  233.              * type of the port later on to PA_SIGNAL whenever we need to wait
  234.              * for a message to come in.
  235.              *
  236.              * The trick used here avoids the need to allocate an extra signal
  237.              * bit for the port. It is quite efficient.
  238.              */
  239.  
  240.             file->af_PacketPort.mp_MsgList.lh_Head     = (struct Node *)&file->af_PacketPort.mp_MsgList.lh_Tail;
  241.             file->af_PacketPort.mp_MsgList.lh_Tail     = NULL;
  242.             file->af_PacketPort.mp_MsgList.lh_TailPred = (struct Node *)&file->af_PacketPort.mp_MsgList.lh_Head;
  243.             file->af_PacketPort.mp_Node.ln_Type        = NT_MSGPORT;
  244.             file->af_PacketPort.mp_Flags               = PA_IGNORE;
  245.             file->af_PacketPort.mp_SigBit              = SIGB_SINGLE;
  246.             file->af_PacketPort.mp_SigTask             = FindTask(NULL);
  247.  
  248.             file->af_Packet.sp_Pkt.dp_Link          = &file->af_Packet.sp_Msg;
  249.             file->af_Packet.sp_Pkt.dp_Arg1          = fh->fh_Arg1;
  250.             file->af_Packet.sp_Pkt.dp_Arg3          = file->af_BufferSize;
  251.             file->af_Packet.sp_Pkt.dp_Res1          = 0;
  252.             file->af_Packet.sp_Pkt.dp_Res2          = 0;
  253.             file->af_Packet.sp_Msg.mn_Node.ln_Name  = (STRPTR)&file->af_Packet.sp_Pkt;
  254.             file->af_Packet.sp_Msg.mn_Node.ln_Type  = NT_MESSAGE;
  255.             file->af_Packet.sp_Msg.mn_Length        = sizeof(struct StandardPacket);
  256.  
  257.             if (accessMode == MODE_READ)
  258.             {
  259.                 /* if we are in read mode, send out the first read packet to
  260.                  * the file system. While the application is getting ready to
  261.                  * read data, the file system will happily fill in this buffer
  262.                  * with DMA transfers, so that by the time the application
  263.                  * needs the data, it will be in the buffer waiting
  264.                  */
  265.  
  266.                 file->af_Packet.sp_Pkt.dp_Type = ACTION_READ;
  267.                 file->af_BytesLeft             = 0;
  268.                 if (file->af_Handler)
  269.                     SendPacket(file,file->af_Buffers[0]);
  270.             }
  271.             else
  272.             {
  273.                 file->af_Packet.sp_Pkt.dp_Type = ACTION_WRITE;
  274.                 file->af_BytesLeft             = file->af_BufferSize;
  275.             }
  276.         }
  277.         else
  278.         {
  279.             Close(handle);
  280.         }
  281.     }
  282.  
  283.     return(file);
  284. }
  285.  
  286.  
  287. /*****************************************************************************/
  288.  
  289.  
  290. LONG CloseAsync(struct AsyncFile *file)
  291. {
  292. LONG result;
  293.  
  294.     if (file)
  295.     {
  296.         result = WaitPacket(file);
  297.         if (result >= 0)
  298.         {
  299.             if (!file->af_ReadMode)
  300.             {
  301.                 /* this will flush out any pending data in the write buffer */
  302.                 if (file->af_BufferSize > file->af_BytesLeft)
  303.                     result = Write(file->af_File,file->af_Buffers[file->af_CurrentBuf],file->af_BufferSize - file->af_BytesLeft);
  304.             }
  305.         }
  306.  
  307.         Close(file->af_File);
  308.         FreeVec(file);
  309.     }
  310.     else
  311.     {
  312.         SetIoErr(ERROR_INVALID_LOCK);
  313.         result = -1;
  314.     }
  315.  
  316.     return(result);
  317. }
  318.  
  319.  
  320. /*****************************************************************************/
  321.  
  322.  
  323. LONG ReadAsync(struct AsyncFile *file, APTR buffer, LONG numBytes)
  324. {
  325. LONG totalBytes;
  326. LONG bytesArrived;
  327.  
  328.     totalBytes = 0;
  329.  
  330.     /* if we need more bytes than there are in the current buffer, enter the
  331.      * read loop
  332.      */
  333.  
  334.     while (numBytes > file->af_BytesLeft)
  335.     {
  336.         /* drain buffer */
  337.         CopyMem(file->af_Offset,buffer,file->af_BytesLeft);
  338.  
  339.         numBytes           -= file->af_BytesLeft;
  340.         buffer              = (APTR)((ULONG)buffer + file->af_BytesLeft);
  341.         totalBytes         += file->af_BytesLeft;
  342.         file->af_BytesLeft  = 0;
  343.  
  344.         bytesArrived = WaitPacket(file);
  345.         if (bytesArrived <= 0)
  346.         {
  347.             if (bytesArrived == 0)
  348.                 return(totalBytes);
  349.  
  350.             return(-1);
  351.         }
  352.  
  353.         /* ask that the buffer be filled */
  354.         SendPacket(file,file->af_Buffers[1-file->af_CurrentBuf]);
  355.  
  356.         if (file->af_SeekOffset > bytesArrived)
  357.             file->af_SeekOffset = bytesArrived;
  358.  
  359.         file->af_Offset      = (APTR)((ULONG)file->af_Buffers[file->af_CurrentBuf] + file->af_SeekOffset);
  360.         file->af_CurrentBuf  = 1 - file->af_CurrentBuf;
  361.         file->af_BytesLeft   = bytesArrived - file->af_SeekOffset;
  362.         file->af_SeekOffset  = 0;
  363.     }
  364.  
  365.     CopyMem(file->af_Offset,buffer,numBytes);
  366.     file->af_BytesLeft -= numBytes;
  367.     file->af_Offset     = (APTR)((ULONG)file->af_Offset + numBytes);
  368.  
  369.     return (totalBytes + numBytes);
  370. }
  371.  
  372.  
  373. /*****************************************************************************/
  374.  
  375.  
  376. LONG ReadCharAsync(struct AsyncFile *file)
  377. {
  378. unsigned char ch;
  379.  
  380.     if (file->af_BytesLeft)
  381.     {
  382.         /* if there is at least a byte left in the current buffer, get it
  383.          * directly. Also update all counters
  384.          */
  385.  
  386.         ch = *(char *)file->af_Offset;
  387.         file->af_BytesLeft--;
  388.         file->af_Offset = (APTR)((ULONG)file->af_Offset + 1);
  389.  
  390.         return((LONG)ch);
  391.     }
  392.  
  393.     /* there were no characters in the current buffer, so call the main read
  394.      * routine. This has the effect of sending a request to the file system to
  395.      * have the current buffer refilled. After that request is done, the
  396.      * character is extracted for the alternate buffer, which at that point
  397.      * becomes the "current" buffer
  398.      */
  399.  
  400.     if (ReadAsync(file,&ch,1) > 0)
  401.         return((LONG)ch);
  402.  
  403.     /* We couldn't read above, so fail */
  404.  
  405.     return(-1);
  406. }
  407.  
  408.  
  409. /*****************************************************************************/
  410.  
  411.  
  412. LONG WriteAsync(struct AsyncFile *file, APTR buffer, LONG numBytes)
  413. {
  414. LONG totalBytes;
  415.  
  416.     totalBytes = 0;
  417.  
  418.     while (numBytes > file->af_BytesLeft)
  419.     {
  420.         /* this takes care of NIL: */
  421.         if (!file->af_Handler)
  422.         {
  423.             file->af_Offset    = file->af_Buffers[0];
  424.             file->af_BytesLeft = file->af_BufferSize;
  425.             return(numBytes);
  426.         }
  427.  
  428.         if (file->af_BytesLeft)
  429.         {
  430.             CopyMem(buffer,file->af_Offset,file->af_BytesLeft);
  431.  
  432.             numBytes   -= file->af_BytesLeft;
  433.             buffer      = (APTR)((ULONG)buffer + file->af_BytesLeft);
  434.             totalBytes += file->af_BytesLeft;
  435.         }
  436.  
  437.         if (WaitPacket(file) < 0)
  438.             return(-1);
  439.  
  440.         /* send the current buffer out to disk */
  441.         SendPacket(file,file->af_Buffers[file->af_CurrentBuf]);
  442.  
  443.         file->af_CurrentBuf = 1 - file->af_CurrentBuf;
  444.         file->af_Offset     = file->af_Buffers[file->af_CurrentBuf];
  445.         file->af_BytesLeft  = file->af_BufferSize;
  446.     }
  447.  
  448.     CopyMem(buffer,file->af_Offset,numBytes);
  449.     file->af_BytesLeft -= numBytes;
  450.     file->af_Offset     = (APTR)((ULONG)file->af_Offset + numBytes);
  451.  
  452.     return (totalBytes + numBytes);
  453. }
  454.  
  455.  
  456. /*****************************************************************************/
  457.  
  458.  
  459. LONG WriteCharAsync(struct AsyncFile *file, UBYTE ch)
  460. {
  461.     if (file->af_BytesLeft)
  462.     {
  463.         /* if there's any room left in the current buffer, directly write
  464.          * the byte into it, updating counters and stuff.
  465.          */
  466.  
  467.         *(UBYTE *)file->af_Offset = ch;
  468.         file->af_BytesLeft--;
  469.         file->af_Offset = (APTR)((ULONG)file->af_Offset + 1);
  470.  
  471.         /* one byte written */
  472.         return(1);
  473.     }
  474.  
  475.     /* there was no room in the current buffer, so call the main write
  476.      * routine. This will effectively send the current buffer out to disk,
  477.      * wait for the other buffer to come back, and then put the byte into
  478.      * it.
  479.      */
  480.  
  481.     return(WriteAsync(file,&ch,1));
  482. }
  483.  
  484.  
  485. /*****************************************************************************/
  486.  
  487.  
  488. LONG SeekAsync(struct AsyncFile *file, LONG position, BYTE mode)
  489. {
  490. LONG  current, target;
  491. LONG  minBuf, maxBuf;
  492. LONG  bytesArrived;
  493. LONG  diff;
  494. LONG  filePos;
  495. LONG  roundTarget;
  496. D_S(struct FileInfoBlock,fib);
  497.  
  498.     bytesArrived = WaitPacket(file);
  499.  
  500.     if (bytesArrived < 0)
  501.         return(-1);
  502.  
  503.     if (file->af_ReadMode)
  504.     {
  505.         /* figure out what the actual file position is */
  506.         filePos = Seek(file->af_File,OFFSET_CURRENT,0);
  507.         if (filePos < 0)
  508.         {
  509.             RecordSyncFailure(file);
  510.             return(-1);
  511.         }
  512.  
  513.         /* figure out what the caller's file position is */
  514.         current = filePos - (file->af_BytesLeft+bytesArrived) + file->af_SeekOffset;
  515.         file->af_SeekOffset = 0;
  516.  
  517.         /* figure out the absolute offset within the file where we must seek to */
  518.         if (mode == MODE_CURRENT)
  519.         {
  520.             target = current + position;
  521.         }
  522.         else if (mode == MODE_START)
  523.         {
  524.             target = position;
  525.         }
  526.         else /* if (mode == MODE_END) */
  527.         {
  528.             if (!ExamineFH(file->af_File,fib))
  529.             {
  530.                 RecordSyncFailure(file);
  531.                 return(-1);
  532.             }
  533.  
  534.             target = fib->fib_Size + position;
  535.         }
  536.  
  537.         /* figure out what range of the file is currently in our buffers */
  538.         minBuf = current - (LONG)((ULONG)file->af_Offset - (ULONG)file->af_Buffers[file->af_CurrentBuf]);
  539.         maxBuf = current + file->af_BytesLeft + bytesArrived;  /* WARNING: this is one too big */
  540.  
  541.         diff = target - current;
  542.  
  543.         if ((target < minBuf) || (target >= maxBuf))
  544.         {
  545.             /* the target seek location isn't currently in our buffers, so
  546.              * move the actual file pointer to the desired location, and then
  547.              * restart the async read thing...
  548.              */
  549.  
  550.             /* this is to keep our file reading block-aligned on the device.
  551.              * block-aligned reads are generally quite a bit faster, so it is
  552.              * worth the trouble to keep things aligned
  553.              */
  554.             roundTarget = (target / file->af_BlockSize) * file->af_BlockSize;
  555.  
  556.             if (Seek(file->af_File,roundTarget-filePos,OFFSET_CURRENT) < 0)
  557.             {
  558.                 RecordSyncFailure(file);
  559.                 return(-1);
  560.             }
  561.  
  562.             SendPacket(file,file->af_Buffers[0]);
  563.  
  564.             file->af_SeekOffset = target-roundTarget;
  565.             file->af_BytesLeft  = 0;
  566.             file->af_CurrentBuf = 0;
  567.             file->af_Offset     = file->af_Buffers[0];
  568.         }
  569.         else if ((target < current) || (diff <= file->af_BytesLeft))
  570.         {
  571.             /* one of the two following things is true:
  572.              *
  573.              * 1. The target seek location is within the current read buffer,
  574.              * but before the current location within the buffer. Move back
  575.              * within the buffer and pretend we never got the pending packet,
  576.              * just to make life easier, and faster, in the read routine.
  577.              *
  578.              * 2. The target seek location is ahead within the current
  579.              * read buffer. Advance to that location. As above, pretend to
  580.              * have never received the pending packet.
  581.              */
  582.  
  583.             RequeuePacket(file);
  584.  
  585.             file->af_BytesLeft -= diff;
  586.             file->af_Offset     = (APTR)((ULONG)file->af_Offset + diff);
  587.         }
  588.         else
  589.         {
  590.             /* at this point, we know the target seek location is within
  591.              * the buffer filled in by the packet that we just received
  592.              * at the start of this function. Throw away all the bytes in the
  593.              * current buffer, send a packet out to get the async thing going
  594.              * again, readjust buffer pointers to the seek location, and return
  595.              * with a grin on your face... :-)
  596.              */
  597.  
  598.             diff -= file->af_BytesLeft;
  599.  
  600.             SendPacket(file,file->af_Buffers[1-file->af_CurrentBuf]);
  601.  
  602.             file->af_Offset    = (APTR)((ULONG)file->af_Buffers[file->af_CurrentBuf] + diff);
  603.             file->af_BytesLeft = bytesArrived - diff;
  604.         }
  605.     }
  606.     else
  607.     {
  608.         if (file->af_BufferSize > file->af_BytesLeft)
  609.         {
  610.             if (Write(file->af_File,file->af_Buffers[file->af_CurrentBuf],file->af_BufferSize - file->af_BytesLeft) < 0)
  611.             {
  612.                 RecordSyncFailure(file);
  613.                 return(-1);
  614.             }
  615.         }
  616.  
  617.         /* this will unfortunately generally result in non block-aligned file
  618.          * access. We could be sneaky and try to resync our file pos at a
  619.          * later time, but we won't bother. Seeking in write-only files is
  620.          * relatively rare (except when writing IFF files with unknown chunk
  621.          * sizes, where the chunk size has to be written after the chunk data)
  622.          */
  623.  
  624.         current = Seek(file->af_File,position,mode);
  625.  
  626.         if (current < 0)
  627.         {
  628.             RecordSyncFailure(file);
  629.             return(-1);
  630.         }
  631.  
  632.         file->af_BytesLeft  = file->af_BufferSize;
  633.         file->af_CurrentBuf = 0;
  634.         file->af_Offset     = file->af_Buffers[0];
  635.     }
  636.  
  637.     return(current);
  638. }
  639.