home *** CD-ROM | disk | FTP | other *** search
/ Amiga ISO Collection / AmigaUtilCD2.iso / Misc / CBMDevKit3.dms / CBMDevKit3.adf / AsynchIO / asyncio.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-11-17  |  20.4 KB  |  642 lines

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