[Contents] [Index] [Help] [Retrace] [Browse <] [Browse >]

;/* ASyncIO.c - Execute me to compile with SAS/C 6.56
sc NMINC STRMERGE NOSTKCHK IGNORE=73 asyncio.c
quit
*/

/*
(c)  Copyright 1992-1999 Amiga, Inc.   All rights reserved.
The information contained herein is subject to change without notice,
and is provided "as is" without warranty of any kind, either expressed
or implied.  The entire risk as to the use of this information is
assumed by the user.
*/

#include <exec/types.h>
#include <exec/exec.h>
#include <dos/dos.h>
#include <dos/dosextens.h>
#include <stdio.h>

#include <clib/exec_protos.h>
#include <clib/dos_protos.h>

#include "asyncio.h"

/*****************************************************************************/

static VOID SendAsync(struct AsyncFile *file, APTR arg2)
{
    /* send out an async packet to the file system. */

    file->af_Packet.sp_Pkt.dp_Port = &file->af_PacketPort;
    file->af_Packet.sp_Pkt.dp_Arg2 = (LONG)arg2;
    PutMsg(file->af_Handler, &file->af_Packet.sp_Msg);
    file->af_PacketPending = TRUE;
}

/*****************************************************************************/


static VOID WaitPacket(struct AsyncFile *file)
{
    /* This enables signalling when a packet comes back to the port */
    file->af_PacketPort.mp_Flags = PA_SIGNAL;

    /* Wait for the packet to come back, and remove it from the message
     * list. Since we know no other packets can come in to the port, we can
     * safely use Remove() instead of GetMsg(). If other packets could come in,
     * we would have to use GetMsg(), which correctly arbitrates access in such
     * a case
     */
    Remove((struct Node *)WaitPort(&file->af_PacketPort));

    /* set the port type back to PA_IGNORE so we won't be bothered with
     * spurious signals
     */
    file->af_PacketPort.mp_Flags = PA_IGNORE;

    /* packet is no longer pending, we got it */
    file->af_PacketPending = FALSE;
}

/*****************************************************************************/

struct AsyncFile *OpenAsync(STRPTR fileName, UBYTE mode, LONG bufferSize)
{
struct AsyncFile  *file;
struct FileHandle *fh;

    /* The buffer size is rounded to a multiple of 32 bytes. This will make
     * DMA as fast as can be
     */

    bufferSize = (bufferSize + 31) & 0xffffffe0;

    /* now allocate the ASyncFile structure, as well as the read buffer. Add
     * 15 bytes to the total size in order to allow for later quad-longword
     * alignement of the buffers
     */

    if (file = AllocVec(sizeof(struct AsyncFile) + bufferSize + 15,
                        MEMF_ANY|MEMF_CLEAR))
    {
        if (mode == MODE_READ)
        {
            file->af_File     = Open(fileName,MODE_OLDFILE);
            file->af_ReadMode = TRUE;
        }
        else if (mode == MODE_WRITE)
        {
            file->af_File = Open(fileName,MODE_NEWFILE);
        }
        else if (mode == MODE_APPEND)
        {
            /* in append mode, we open for writing, and then seek to the
             * end of the file. That way, the initial write will happen at
             * the end of the file, thus extending it
             */

            if (file->af_File = Open(fileName,MODE_READWRITE))
            {
                if (Seek(file->af_File,0,OFFSET_END) < 0)
                {
                    Close(file->af_File);
                    file->af_File = NULL;
                }
            }
        }

        if (!file->af_File)
        {
            /* file didn't open, free stuff and leave */
            FreeVec(file);
            return(NULL);
        }

        /* initialize the ASyncFile structure. We do as much as we can here,
         * in order to avoid doing it in more critical sections
         *
         * Note how the two buffers used are quad-longword aligned. This helps
         * performance on 68040 systems with copyback cache. Aligning the data
         * avoids a nasty side-effect of the 040 caches on DMA. Not aligning
         * the data causes the device driver to have to do some magic to avoid
         * the cache problem. This magic will generally involve flushing the
         * CPU caches. This is very costly on an 040. Aligning things avoids
         * the need for magic, at the cost of at most 15 bytes of ram.
         */

        fh                  = BADDR(file->af_File);
        file->af_Handler    = fh->fh_Type;
        file->af_BufferSize = bufferSize / 2;
        file->af_Buffers[0] =
                (APTR)(((ULONG)file + sizeof(struct AsyncFile) + 15) & 0xfffffff0);
        file->af_Buffers[1] =
                (APTR)((ULONG)file->af_Buffers[0] + file->af_BufferSize);
        file->af_Offset     = file->af_Buffers[0];

        /* this is the port used to get the packets we send out back.
         * It is initialized to PA_IGNORE, which means that no signal is
         * generated when a message comes in to the port. The signal bit number
         * is initialized to SIGB_SINGLE, which is the special bit that can
         * be used for one-shot signalling. The signal will never be set,
         * since the port is of type PA_IGNORE. We'll change the type of the
         * port later on to PA_SIGNAL whenever we need to wait for a message
         * to come in.
         *
         * The trick used here avoids the need to allocate an extra signal bit
         * for the port. It is quite efficient.
         */

        file->af_PacketPort.mp_MsgList.lh_Head     =
                (struct Node *)&file->af_PacketPort.mp_MsgList.lh_Tail;
        file->af_PacketPort.mp_MsgList.lh_TailPred =
                (struct Node *)&file->af_PacketPort.mp_MsgList.lh_Head;
        file->af_PacketPort.mp_Node.ln_Type        = NT_MSGPORT;
        file->af_PacketPort.mp_Flags               = PA_IGNORE;
        file->af_PacketPort.mp_SigBit              = SIGB_SINGLE;
        file->af_PacketPort.mp_SigTask             = FindTask(NULL);

        file->af_Packet.sp_Pkt.dp_Link          = &file->af_Packet.sp_Msg;
        file->af_Packet.sp_Pkt.dp_Arg1          = fh->fh_Arg1;
        file->af_Packet.sp_Pkt.dp_Arg3          = file->af_BufferSize;
        file->af_Packet.sp_Msg.mn_Node.ln_Name  = (STRPTR)&file->af_Packet.sp_Pkt;
        file->af_Packet.sp_Msg.mn_Node.ln_Type  = NT_MESSAGE;
        file->af_Packet.sp_Msg.mn_Length        = sizeof(struct StandardPacket);

        if (mode == MODE_READ)
        {
            /* if we are in read mode, send out the first read packet to the
             * file system. While the application is getting ready to read
             * data, the file system will happily fill in this buffer with
             * DMA transfer, so that by the time the application needs the data,
             * it will be in the buffer waiting
             */

            file->af_Packet.sp_Pkt.dp_Type = ACTION_READ;
            if (file->af_Handler)
                SendAsync(file,file->af_Buffers[0]);
        }
        else
        {
            file->af_Packet.sp_Pkt.dp_Type = ACTION_WRITE;
            file->af_BytesLeft             = file->af_BufferSize;
        }
    }

    return(file);
}

/*****************************************************************************/

LONG CloseAsync(struct AsyncFile *file)
{
LONG result;
LONG result2;

    result = 0;
    if (file)
    {
        if (file->af_PacketPending)
            WaitPacket(file);

        result  = file->af_Packet.sp_Pkt.dp_Res1;
        result2 = file->af_Packet.sp_Pkt.dp_Res2;
        if (result >= 0)
        {
            if (!file->af_ReadMode)
            {
                /* this will flush out any pending data in the write buffer */
                result  = Write(file->af_File,
                          file->af_Buffers[file->af_CurrentBuf],
                          file->af_BufferSize - file->af_BytesLeft);
                result2 = IoErr();
            }
        }

        Close(file->af_File);
        FreeVec(file);

        SetIoErr(result2);
    }

    return(result);
}

/*****************************************************************************/

LONG ReadAsync(struct AsyncFile *file, APTR buf, LONG numBytes)
{
LONG totalBytes;
LONG bytesArrived;

    totalBytes = 0;

    /* if we need more bytes than there are in the current buffer, enter the
     * read loop
     */

    while (numBytes > file->af_BytesLeft)
    {
        /* this takes care of NIL: */
        if (!file->af_Handler)
            return(0);

        WaitPacket(file);

        bytesArrived = file->af_Packet.sp_Pkt.dp_Res1;
        if (bytesArrived <= 0)
        {
            /* error, get out of here */
            SetIoErr(file->af_Packet.sp_Pkt.dp_Res2);
            return(-1);
        }

        /* enable this section of code if you want special processing for
         * reads bigger than the buffer size
         */
#ifdef OPTIMIZE_BIG_READS
        if (numBytes > file->af_BytesLeft + bytesArrived + file->af_BufferSize)
        {
            if (file->af_BytesLeft)
            {
                CopyMem(file->af_Offset,buf,file->af_BytesLeft);

                numBytes   -= file->af_BytesLeft;
                buf         = (APTR)((ULONG)buf + file->af_BytesLeft);
                totalBytes += file->af_BytesLeft;
                file->af_BytesLeft = 0;
            }

            if (bytesArrived)
            {
                CopyMem(file->af_Buffers[file->af_CurrentBuf],buf,bytesArrived);

                numBytes   -= bytesArrived;
                buf         = (APTR)((ULONG)buf + bytesArrived);
                totalBytes += bytesArrived;
            }

            bytesArrived = Read(file->af_File,buf,numBytes);

            if (bytesArrived <= 0)
                return(-1);

            SendAsync(file,file->af_Buffers[0]);
            file->af_CurrentBuf = 0;
            file->af_BytesLeft  = 0;

            return(totalBytes + bytesArrived);
        }
#endif

        if (file->af_BytesLeft)
        {
            CopyMem(file->af_Offset,buf,file->af_BytesLeft);

            numBytes   -= file->af_BytesLeft;
            buf         = (APTR)((ULONG)buf + file->af_BytesLeft);
            totalBytes += file->af_BytesLeft;
        }

        /* ask that the buffer be filled */
        SendAsync(file,file->af_Buffers[1-file->af_CurrentBuf]);

        file->af_Offset     = file->af_Buffers[file->af_CurrentBuf];
        file->af_CurrentBuf = 1 - file->af_CurrentBuf;
        file->af_BytesLeft  = bytesArrived;
    }

    if (numBytes)
    {
        CopyMem(file->af_Offset,buf,numBytes);
        file->af_BytesLeft -= numBytes;
        file->af_Offset     = (APTR)((ULONG)file->af_Offset + numBytes);
    }

    return (totalBytes + numBytes);
}

/*****************************************************************************/

LONG ReadCharAsync(struct AsyncFile *file)
{
char ch;

    if (file->af_BytesLeft)
    {
        /* if there is at least a byte left in the current buffer, get it
         * directly. Also update all counters
         */

        ch = *(char *)file->af_Offset;
        file->af_BytesLeft--;
        file->af_Offset = (APTR)((ULONG)file->af_Offset + 1);

        return((LONG)ch);
    }

    /* there were no characters in the current buffer, so call the main read
     * routine. This has the effect of sending a request to the file system to
     * have the current buffer refilled. After that request is done, the
     * character is extracted for the alternate buffer, which at that point
     * becomes the "current" buffer
     */

    if (ReadAsync(file,&ch,1) > 0)
        return((LONG)ch);

    /* We couldn't read above, so fail */

    return(-1);
}

/*****************************************************************************/

LONG WriteAsync(struct AsyncFile *file, APTR buf, LONG numBytes)
{
LONG totalBytes;

    totalBytes = 0;

    while (numBytes > file->af_BytesLeft)
    {
        /* this takes care of NIL: */
        if (!file->af_Handler)
        {
            file->af_Offset    = file->af_Buffers[file->af_CurrentBuf];
            file->af_BytesLeft = file->af_BufferSize;
            return(numBytes + totalBytes);
        }

        if (file->af_BytesLeft)
        {
            CopyMem(buf,file->af_Offset,numBytes);

            numBytes   -= file->af_BytesLeft;
            buf         = (APTR)((ULONG)buf + file->af_BytesLeft);
            totalBytes += file->af_BytesLeft;
        }

        if (file->af_PacketPending)
        {
            WaitPacket(file);

            if (file->af_Packet.sp_Pkt.dp_Res1 <= 0)
            {
                /* an error occurred, leave */
                SetIoErr(file->af_Packet.sp_Pkt.dp_Res2);
                return(-1);
            }
        }

        /* send the current buffer out to disk */
        SendAsync(file,file->af_Buffers[file->af_CurrentBuf]);

        file->af_CurrentBuf   = 1 - file->af_CurrentBuf;
        file->af_Offset       = file->af_Buffers[file->af_CurrentBuf];
        file->af_BytesLeft    = file->af_BufferSize;
    }

    if (numBytes)
    {
        CopyMem(buf,file->af_Offset,numBytes);
        file->af_BytesLeft -= numBytes;
        file->af_Offset     = (APTR)((ULONG)file->af_Offset + numBytes);
    }

    return (totalBytes + numBytes);
}

/*****************************************************************************/

LONG WriteCharAsync(struct AsyncFile *file, char ch)
{
    if (file->af_BytesLeft)
    {
        /* if there's any room left in the current buffer, directly write
         * the byte into it, updating counters and stuff.
         */

        *(char *)file->af_Offset = ch;
        file->af_BytesLeft--;
        file->af_Offset = (APTR)((ULONG)file->af_Offset + 1);

        /* one byte written */
        return(1);
    }

    /* there was no room in the current buffer, so call the main write
     * routine. This will effectively send the current buffer out to disk,
     * wait for the other buffer to come back, and then put the byte into
     * it.
     */

    return(WriteAsync(file,&ch,1));
}


[Back to Amiga Developer Docs]