home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format 106 / af106sub.adf / datatypes.LZX / anim_datatype / ASYNCIO.C < prev    next >
C/C++ Source or Header  |  1983-01-16  |  17KB  |  514 lines

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