//------------------------------------------------------------------------------

// File: AsyncIo.h

//

// Desc: DirectShow sample code - base library for I/O functionality.

//

// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.

//------------------------------------------------------------------------------





#ifndef __ASYNCIO_H__

#define __ASYNCIO_H__

//

// definition of CAsyncFile object that performs file access. It provides

// asynchronous, unbuffered, aligned reads from a file, using a worker thread

// on win95 and potentially overlapped i/o if available.



// !!! Need to use real overlapped i/o if available

// currently only uses worker thread, not overlapped i/o





class CAsyncIo;

class CAsyncStream;



//

//  Model the stream we read from based on a file-like interface

//

class CAsyncStream

{

public:

    virtual ~CAsyncStream() {};

    virtual HRESULT SetPointer(LONGLONG llPos) = 0;

    virtual HRESULT Read(PBYTE pbBuffer,

                         DWORD dwBytesToRead,

                         BOOL bAlign,

                         LPDWORD pdwBytesRead) = 0;

    virtual LONGLONG Size(LONGLONG *pSizeAvailable = NULL) = 0;

    virtual DWORD Alignment() = 0;

    virtual void Lock() = 0;

    virtual void Unlock() = 0;

    //virtual void SetStopHandle(HANDLE hevStop) {}

};



// represents a single request and performs the i/o. Can be called on either

// worker thread or app thread, but must hold pcsFile across file accesses.

// (ie across SetFilePointer/ReadFile pairs)

class CAsyncRequest

{

    CAsyncIo *m_pIo;

    CAsyncStream *m_pStream;

    LONGLONG      m_llPos;

    BOOL        m_bAligned;

    LONG 	m_lLength;

    BYTE* 	m_pBuffer;

    LPVOID 	m_pContext;

    DWORD	m_dwUser;

    HRESULT     m_hr;



public:

    // init the params for this request. Issue the i/o

    // if overlapped i/o is possible.

    HRESULT Request(

    	CAsyncIo *pIo,

        CAsyncStream *pStream,

    	LONGLONG llPos,

	LONG lLength,

        BOOL bAligned,

	BYTE* pBuffer,

	LPVOID pContext,	// filter's context

	DWORD dwUser);		// downstream filter's context



    // issue the i/o if not overlapped, and block until i/o complete.

    // returns error code of file i/o

    HRESULT Complete();



    // cancels the i/o. blocks until i/o is no longer pending

    HRESULT Cancel()

    {

	return S_OK;

    };



    // accessor functions

    LPVOID GetContext()

    {

    	return m_pContext;

    };



    DWORD GetUser()

    {

	return m_dwUser;

    };



    HRESULT GetHResult() {

        return m_hr;

    };



    // we set m_lLength to the actual length

    LONG GetActualLength() {

        return m_lLength;

    };



    LONGLONG GetStart() {

        return m_llPos;

    };

};





typedef CGenericList<CAsyncRequest> CRequestList;



// this class needs a worker thread, but the ones defined in classes\base

// are not suitable (they assume you have one message sent or posted per

// request, whereas here for efficiency we want just to set an event when

// there is work on the queue).

//

// we create CAsyncRequest objects and queue them on m_listWork. The worker

// thread pulls them off, completes them and puts them on m_listDone.

// The events m_evWork and m_evDone are set when the corresponding lists are

// not empty.

//

// Synchronous requests are done on the caller thread. These should be

// synchronised by the caller, but to make sure we hold m_csFile across

// the SetFilePointer/ReadFile code.

//

// Flush by calling BeginFlush. This rejects all further requests (by

// setting m_bFlushing within m_csLists), cancels all requests and moves them

// to the done list, and sets m_evDone to ensure that no WaitForNext operations

// will block. Call EndFlush to cancel this state.

//

// we support unaligned calls to SyncRead. This is done by opening the file

// twice if we are using unbuffered i/o (m_dwAlign > 1).

// !!!fix this to buffer on top of existing file handle?

class CAsyncIo

{



    CCritSec m_csReader;

    CAsyncStream *m_pStream;



    CCritSec m_csLists;      // locks access to the list and events

    BOOL m_bFlushing;        // true if between BeginFlush/EndFlush

    CRequestList m_listWork;

    CRequestList m_listDone;

    CAMEvent m_evWork;         // set when list is not empty

    CAMEvent m_evDone;



    // for correct flush behaviour: all protected by m_csLists

    LONG    m_cItemsOut;    // nr of items not on listDone or listWork

    BOOL    m_bWaiting;     // TRUE if someone waiting for m_evAllDone

    CAMEvent m_evAllDone;   // signal when m_cItemsOut goes to 0 if m_cWaiting





    CAMEvent m_evStop;         // set when thread should exit

    HANDLE m_hThread;



    LONGLONG Size() {

        ASSERT(m_pStream != NULL);

        return m_pStream->Size();

    };



    // start the thread

    HRESULT StartThread(void);



    // stop the thread and close the handle

    HRESULT CloseThread(void);



    // manage the list of requests. hold m_csLists and ensure

    // that the (manual reset) event hevList is set when things on

    // the list but reset when the list is empty.

    // returns null if list empty

    CAsyncRequest* GetWorkItem();



    // get an item from the done list

    CAsyncRequest* GetDoneItem();



    // put an item on the work list

    HRESULT PutWorkItem(CAsyncRequest* pRequest);



    // put an item on the done list

    HRESULT PutDoneItem(CAsyncRequest* pRequest);



    // called on thread to process any active requests

    void ProcessRequests(void);



    // initial static thread proc calls ThreadProc with DWORD

    // param as this

    static DWORD WINAPI InitialThreadProc(LPVOID pv) {

	CAsyncIo * pThis = (CAsyncIo*) pv;

	return pThis->ThreadProc();

    };



    DWORD ThreadProc(void);



public:



    CAsyncIo(CAsyncStream *pStream);

    ~CAsyncIo();



    // open the file

    HRESULT Open(LPCTSTR pName);



    // ready for async activity - call this before

    // calling Request

    HRESULT AsyncActive(void);



    // call this when no more async activity will happen before

    // the next AsyncActive call

    HRESULT AsyncInactive(void);



    // queue a requested read. must be aligned.

    HRESULT Request(

	    	LONGLONG llPos,

		LONG lLength,

                BOOL bAligned,

		BYTE* pBuffer,

		LPVOID pContext,

		DWORD dwUser);



    // wait for the next read to complete

    HRESULT WaitForNext(

	    	DWORD dwTimeout,

		LPVOID *ppContext,

		DWORD * pdwUser,

                LONG * pcbActual

                );



    // perform a read of an already aligned buffer

    HRESULT SyncReadAligned(

	    	LONGLONG llPos,

		LONG lLength,

		BYTE* pBuffer,

                LONG* pcbActual,

                PVOID pvContext

                );



    // perform a synchronous read. will be buffered

    // if not aligned.

    HRESULT SyncRead(

                LONGLONG llPos,

                LONG lLength,

                BYTE* pBuffer);



    // return length

    HRESULT Length(LONGLONG *pllTotal, LONGLONG* pllAvailable);



    // all Reader positions, read lengths and memory locations must

    // be aligned to this.

    HRESULT Alignment(LONG* pl);



    HRESULT BeginFlush();

    HRESULT EndFlush();



    LONG Alignment()

    {

        return m_pStream->Alignment();

    };



    BOOL IsAligned(LONG l) {

	if ((l & (Alignment() -1)) == 0) {

	    return TRUE;

	} else {

	    return FALSE;

	}

    };



    BOOL IsAligned(LONGLONG ll) {

	return IsAligned( (LONG) (ll & 0xffffffff));

    };



    //  Accessor

    HANDLE StopEvent() const { return m_evDone; }

};



#endif // __ASYNCIO_H__

