home *** CD-ROM | disk | FTP | other *** search
/ YPA: Your Privacy Assured / YPA.ISO / other_goodies / utilities / amipop116.lha / AmiPOP116 / source / asyncio.c next >
C/C++ Source or Header  |  1994-12-05  |  21KB  |  631 lines

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