git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@377 0109f412-320b-0410-ab79-c3e0c5ffbbe6
605 lines
14 KiB
C++
605 lines
14 KiB
C++
//------------------------------------------------------------------------------
|
|
// Desc: This file contains the F_IOBuffer and F_IOBufferMgr classes.
|
|
//
|
|
// Tabs: 3
|
|
//
|
|
// Copyright (c) 2001-2006 Novell, Inc. All Rights Reserved.
|
|
//
|
|
// This program is free software; you can redistribute it and/or
|
|
// modify it under the terms of version 2 of the GNU General Public
|
|
// License as published by the Free Software Foundation.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program; if not, contact Novell, Inc.
|
|
//
|
|
// To contact Novell about this file by physical or electronic mail,
|
|
// you may find current contact information at www.novell.com
|
|
//
|
|
// $Id: fbuff.cpp 3111 2006-01-19 13:10:50 -0700 (Thu, 19 Jan 2006) dsanders $
|
|
//------------------------------------------------------------------------------
|
|
|
|
#include "ftksys.h"
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
class F_IOBufferMgr : public IF_IOBufferMgr
|
|
{
|
|
public:
|
|
|
|
F_IOBufferMgr();
|
|
|
|
virtual ~F_IOBufferMgr();
|
|
|
|
RCODE FLMAPI waitForAllPendingIO( void);
|
|
|
|
FINLINE void FLMAPI setMaxBuffers(
|
|
FLMUINT uiMaxBuffers)
|
|
{
|
|
m_uiMaxBuffers = uiMaxBuffers;
|
|
}
|
|
|
|
FINLINE void FLMAPI setMaxBytes(
|
|
FLMUINT uiMaxBytes)
|
|
{
|
|
m_uiMaxBufferBytesToUse = uiMaxBytes;
|
|
}
|
|
|
|
FINLINE void FLMAPI enableKeepBuffer( void)
|
|
{
|
|
m_bKeepBuffers = TRUE;
|
|
}
|
|
|
|
RCODE FLMAPI getBuffer(
|
|
IF_IOBuffer ** ppIOBuffer,
|
|
FLMUINT uiBufferSize,
|
|
FLMUINT uiBlockSize);
|
|
|
|
FINLINE FLMBOOL FLMAPI havePendingIO( void)
|
|
{
|
|
return( m_pFirstPending ? TRUE : FALSE);
|
|
}
|
|
|
|
FINLINE FLMBOOL FLMAPI haveUsed( void)
|
|
{
|
|
return( m_pFirstUsed ? TRUE : FALSE);
|
|
}
|
|
|
|
private:
|
|
|
|
// Private methods and variables
|
|
|
|
F_IOBuffer * m_pFirstPending;
|
|
F_IOBuffer * m_pFirstAvail;
|
|
F_IOBuffer * m_pFirstUsed;
|
|
FLMUINT m_uiMaxBuffers;
|
|
FLMUINT m_uiMaxBufferBytesToUse;
|
|
FLMUINT m_uiBufferBytesInUse;
|
|
FLMUINT m_uiBuffersInUse;
|
|
RCODE m_completionRc;
|
|
FLMBOOL m_bKeepBuffers;
|
|
|
|
void linkToList(
|
|
F_IOBuffer ** ppListHead,
|
|
F_IOBuffer * pIOBuffer);
|
|
|
|
void unlinkFromList(
|
|
F_IOBuffer * pIOBuffer);
|
|
|
|
friend class F_IOBuffer;
|
|
|
|
};
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
RCODE FLMAPI FlmAllocIOBufferMgr(
|
|
IF_IOBufferMgr ** ppIOBufferMgr)
|
|
{
|
|
if( (*ppIOBufferMgr = f_new F_IOBufferMgr) == NULL)
|
|
{
|
|
return( RC_SET( NE_FLM_MEM));
|
|
}
|
|
|
|
return( NE_FLM_OK);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
F_IOBufferMgr::F_IOBufferMgr()
|
|
{
|
|
m_pFirstPending = NULL;
|
|
m_pFirstAvail = NULL;
|
|
m_pFirstUsed = NULL;
|
|
m_uiMaxBuffers = 0;
|
|
m_uiMaxBufferBytesToUse = 0;
|
|
m_uiBufferBytesInUse = 0;
|
|
m_uiBuffersInUse = 0;
|
|
m_completionRc = NE_FLM_OK;
|
|
m_bKeepBuffers = FALSE;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
F_IOBufferMgr::~F_IOBufferMgr()
|
|
{
|
|
f_assert( !m_pFirstPending && !m_pFirstUsed);
|
|
while (m_pFirstPending)
|
|
{
|
|
m_pFirstPending->Release();
|
|
}
|
|
|
|
while (m_pFirstAvail)
|
|
{
|
|
m_pFirstAvail->Release();
|
|
}
|
|
|
|
while (m_pFirstUsed)
|
|
{
|
|
m_pFirstUsed->Release();
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
RCODE FLMAPI F_IOBufferMgr::waitForAllPendingIO( void)
|
|
{
|
|
RCODE rc = NE_FLM_OK;
|
|
F_IOBuffer * pBuf;
|
|
|
|
while( (pBuf = m_pFirstPending) != NULL)
|
|
{
|
|
(void)pBuf->waitToComplete();
|
|
}
|
|
|
|
rc = m_completionRc;
|
|
m_completionRc = NE_FLM_OK;
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
void F_IOBufferMgr::linkToList(
|
|
F_IOBuffer ** ppListHead,
|
|
F_IOBuffer * pIOBuffer)
|
|
{
|
|
f_assert( pIOBuffer->m_eList == F_IOBuffer::MGR_LIST_NONE);
|
|
pIOBuffer->m_pPrev = NULL;
|
|
if ((pIOBuffer->m_pNext = *ppListHead) != NULL)
|
|
{
|
|
(*ppListHead)->m_pPrev = pIOBuffer;
|
|
}
|
|
*ppListHead = pIOBuffer;
|
|
if (ppListHead == &m_pFirstPending ||
|
|
ppListHead == &m_pFirstUsed)
|
|
{
|
|
pIOBuffer->m_eList = (ppListHead == &m_pFirstPending
|
|
? F_IOBuffer::MGR_LIST_PENDING
|
|
: F_IOBuffer::MGR_LIST_USED);
|
|
pIOBuffer->m_bDeleteOnNotify = (m_bKeepBuffers
|
|
? FALSE
|
|
: TRUE);
|
|
m_uiBuffersInUse++;
|
|
m_uiBufferBytesInUse += pIOBuffer->m_uiBufferSize;
|
|
}
|
|
else
|
|
{
|
|
pIOBuffer->m_eList = F_IOBuffer::MGR_LIST_AVAIL;
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
void F_IOBufferMgr::unlinkFromList(
|
|
F_IOBuffer * pIOBuffer)
|
|
{
|
|
if (pIOBuffer->m_pNext)
|
|
{
|
|
pIOBuffer->m_pNext->m_pPrev = pIOBuffer->m_pPrev;
|
|
}
|
|
if (pIOBuffer->m_pPrev)
|
|
{
|
|
pIOBuffer->m_pPrev->m_pNext = pIOBuffer->m_pNext;
|
|
}
|
|
else if (pIOBuffer->m_eList == F_IOBuffer::MGR_LIST_AVAIL)
|
|
{
|
|
m_pFirstAvail = pIOBuffer->m_pNext;
|
|
}
|
|
else if (pIOBuffer->m_eList == F_IOBuffer::MGR_LIST_PENDING)
|
|
{
|
|
m_pFirstPending = pIOBuffer->m_pNext;
|
|
}
|
|
else if (pIOBuffer->m_eList == F_IOBuffer::MGR_LIST_USED)
|
|
{
|
|
m_pFirstUsed = pIOBuffer->m_pNext;
|
|
}
|
|
else
|
|
{
|
|
f_assert( 0);
|
|
}
|
|
if (pIOBuffer->m_eList == F_IOBuffer::MGR_LIST_PENDING ||
|
|
pIOBuffer->m_eList == F_IOBuffer::MGR_LIST_USED)
|
|
{
|
|
m_uiBuffersInUse--;
|
|
f_assert( m_uiBufferBytesInUse >= pIOBuffer->m_uiBufferSize);
|
|
m_uiBufferBytesInUse -= pIOBuffer->m_uiBufferSize;
|
|
}
|
|
pIOBuffer->m_eList = F_IOBuffer::MGR_LIST_NONE;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
RCODE FLMAPI F_IOBufferMgr::getBuffer(
|
|
IF_IOBuffer ** ppIOBuffer,
|
|
FLMUINT uiBufferSize,
|
|
FLMUINT uiBlockSize)
|
|
{
|
|
RCODE rc = NE_FLM_OK;
|
|
F_IOBuffer * pIOBuffer = NULL;
|
|
F_IOBuffer * pBuf;
|
|
|
|
if( RC_BAD( m_completionRc))
|
|
{
|
|
rc = m_completionRc;
|
|
goto Exit;
|
|
}
|
|
|
|
if ((m_uiBufferBytesInUse + uiBufferSize > m_uiMaxBufferBytesToUse &&
|
|
m_pFirstPending) ||
|
|
m_uiBuffersInUse == m_uiMaxBuffers)
|
|
{
|
|
pBuf = m_pFirstPending;
|
|
for (;;)
|
|
{
|
|
if( pBuf->isIOComplete())
|
|
{
|
|
if( RC_BAD( rc = pBuf->waitToComplete()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
pBuf = m_pFirstPending;
|
|
if (m_uiBufferBytesInUse + uiBufferSize > m_uiMaxBufferBytesToUse &&
|
|
m_pFirstPending)
|
|
{
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
f_assert( m_uiBuffersInUse < m_uiMaxBuffers);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((pBuf = pBuf->m_pNext) == NULL)
|
|
{
|
|
f_yieldCPU();
|
|
pBuf = m_pFirstPending;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If we are set up to keep buffers, caller better always ask
|
|
// for the same size.
|
|
|
|
if (m_pFirstAvail)
|
|
{
|
|
pIOBuffer = m_pFirstAvail;
|
|
unlinkFromList( pIOBuffer);
|
|
f_assert( pIOBuffer->getBufferSize() == uiBufferSize);
|
|
}
|
|
else
|
|
{
|
|
if ((pIOBuffer = f_new F_IOBuffer) == NULL)
|
|
{
|
|
rc = RC_SET( NE_FLM_MEM);
|
|
goto Exit;
|
|
}
|
|
pIOBuffer->m_pIOBufferMgr = this;
|
|
if (RC_BAD( rc = pIOBuffer->setupBuffer( uiBufferSize,
|
|
uiBlockSize)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// An F_IOBuffer object, once created must ALWAYS be linked
|
|
// into the buffer manager's used list.
|
|
|
|
linkToList( &m_pFirstUsed, pIOBuffer);
|
|
|
|
Exit:
|
|
|
|
if (RC_BAD( rc) && pIOBuffer)
|
|
{
|
|
pIOBuffer->Release();
|
|
pIOBuffer = NULL;
|
|
}
|
|
*ppIOBuffer = pIOBuffer;
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
F_IOBuffer::F_IOBuffer()
|
|
{
|
|
m_pIOBufferMgr = NULL;
|
|
m_pucBuffer = NULL;
|
|
#ifdef FLM_DEBUG
|
|
f_memset( m_UserData, 0, sizeof( m_UserData));
|
|
#endif
|
|
m_uiBufferSize = 0;
|
|
m_uiBlockSize = 0;
|
|
m_eList = MGR_LIST_NONE;
|
|
m_bDeleteOnNotify = TRUE;
|
|
m_pNext = NULL;
|
|
m_pPrev = NULL;
|
|
m_fnCompletion = NULL;
|
|
m_completionRc = NE_FLM_OK;
|
|
#if defined( FLM_WIN)
|
|
m_FileHandle = INVALID_HANDLE_VALUE;
|
|
m_Overlapped.hEvent = 0;
|
|
#elif defined( FLM_LINUX) || defined( FLM_SOLARIS)
|
|
m_aio.aio_fildes = -1;
|
|
#endif
|
|
m_pStats = NULL;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
F_IOBuffer::~F_IOBuffer()
|
|
{
|
|
// Unlink from list object is in, if any.
|
|
|
|
if (m_eList != MGR_LIST_NONE)
|
|
{
|
|
f_assert( m_pIOBufferMgr);
|
|
m_pIOBufferMgr->unlinkFromList( this);
|
|
}
|
|
|
|
#if defined( FLM_WIN)
|
|
if( m_Overlapped.hEvent)
|
|
{
|
|
CloseHandle( m_Overlapped.hEvent);
|
|
}
|
|
#endif
|
|
|
|
if (m_pucBuffer)
|
|
{
|
|
#ifdef FLM_WIN
|
|
(void)VirtualFree( m_pucBuffer, 0, MEM_RELEASE);
|
|
m_pucBuffer = NULL;
|
|
#elif defined( FLM_LINUX) || defined( FLM_SOLARIS)
|
|
free( m_pucBuffer);
|
|
m_pucBuffer = NULL;
|
|
#else
|
|
f_free( &m_pucBuffer);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
void FLMAPI F_IOBuffer::makePending( void)
|
|
{
|
|
f_assert( m_eList == MGR_LIST_USED);
|
|
|
|
// Unlink from used list
|
|
|
|
m_pIOBufferMgr->unlinkFromList( this);
|
|
|
|
// Link into pending list.
|
|
|
|
m_pIOBufferMgr->linkToList( &m_pIOBufferMgr->m_pFirstPending, this);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
RCODE FLMAPI F_IOBuffer::setupBuffer(
|
|
FLMUINT uiBufferSize,
|
|
FLMUINT uiBlockSize)
|
|
{
|
|
RCODE rc = NE_FLM_OK;
|
|
|
|
#if defined( FLM_WIN)
|
|
if( (m_Overlapped.hEvent = CreateEvent( NULL, TRUE,
|
|
FALSE, NULL)) == NULL)
|
|
{
|
|
rc = f_mapPlatformError( GetLastError(), NE_FLM_SETTING_UP_FOR_WRITE);
|
|
goto Exit;
|
|
}
|
|
#endif
|
|
|
|
// Allocate a buffer
|
|
|
|
#ifdef FLM_WIN
|
|
if ((m_pucBuffer = (FLMBYTE *)VirtualAlloc( NULL,
|
|
(DWORD)uiBufferSize,
|
|
MEM_COMMIT, PAGE_READWRITE)) == NULL)
|
|
{
|
|
rc = f_mapPlatformError( GetLastError(), NE_FLM_MEM);
|
|
goto Exit;
|
|
}
|
|
#elif defined( FLM_LINUX)
|
|
if( posix_memalign( (void **)&m_pucBuffer,
|
|
sysconf( _SC_PAGESIZE), uiBufferSize) != 0)
|
|
{
|
|
rc = f_mapPlatformError( errno, NE_FLM_MEM);
|
|
goto Exit;
|
|
}
|
|
#elif defined( FLM_SOLARIS)
|
|
if( (m_pucBuffer = (FLMBYTE *)memalign( sysconf( _SC_PAGESIZE),
|
|
uiBufferSize)) == NULL)
|
|
{
|
|
rc = f_mapPlatformError( errno, NE_FLM_MEM);
|
|
goto Exit;
|
|
}
|
|
#else
|
|
if (RC_BAD( rc = f_alloc( uiBufferSize, &m_pucBuffer)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
#endif
|
|
|
|
m_uiBufferSize = uiBufferSize;
|
|
m_uiBlockSize = uiBlockSize;
|
|
|
|
Exit:
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
void FLMAPI F_IOBuffer::notifyComplete(
|
|
RCODE rc)
|
|
{
|
|
f_assert( m_eList == MGR_LIST_PENDING ||
|
|
m_eList == MGR_LIST_USED);
|
|
|
|
m_completionRc = rc;
|
|
if( m_fnCompletion)
|
|
{
|
|
m_fnCompletion( this);
|
|
|
|
// Fix so completion callback won't be called twice.
|
|
|
|
m_fnCompletion = NULL;
|
|
}
|
|
|
|
if (RC_BAD( rc) && RC_OK( m_pIOBufferMgr->m_completionRc))
|
|
{
|
|
m_pIOBufferMgr->m_completionRc = rc;
|
|
}
|
|
|
|
if (m_bDeleteOnNotify)
|
|
{
|
|
Release();
|
|
}
|
|
else
|
|
{
|
|
m_pIOBufferMgr->unlinkFromList( this);
|
|
m_pIOBufferMgr->linkToList( &m_pIOBufferMgr->m_pFirstAvail, this);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
FLMBOOL F_IOBuffer::isIOComplete( void)
|
|
{
|
|
FLMBOOL bComplete = FALSE;
|
|
|
|
if( m_eList != MGR_LIST_PENDING)
|
|
{
|
|
bComplete = TRUE;
|
|
goto Exit;
|
|
}
|
|
|
|
#ifdef FLM_WIN
|
|
if (m_FileHandle == INVALID_HANDLE_VALUE ||
|
|
HasOverlappedIoCompleted( &m_Overlapped))
|
|
{
|
|
bComplete = TRUE;
|
|
}
|
|
#endif
|
|
|
|
#if defined( FLM_LINUX) || defined( FLM_SOLARIS)
|
|
if( m_aio.aio_fildes == -1 || aio_error( &m_aio) != EINPROGRESS)
|
|
{
|
|
bComplete = TRUE;
|
|
}
|
|
#endif
|
|
|
|
Exit:
|
|
|
|
return( bComplete);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
RCODE F_IOBuffer::waitToComplete( void)
|
|
{
|
|
RCODE rc = NE_FLM_OK;
|
|
|
|
// IMPORTANT NOTE! The call to notifyComplete will destroy this
|
|
// object, so nothing in the object can be accessed after notifyComplete
|
|
// is called.
|
|
|
|
#ifdef FLM_WIN
|
|
if (m_FileHandle != INVALID_HANDLE_VALUE)
|
|
{
|
|
DWORD udBytesWritten;
|
|
|
|
if (!GetOverlappedResult( m_FileHandle, &m_Overlapped,
|
|
&udBytesWritten, TRUE))
|
|
{
|
|
rc = f_mapPlatformError( GetLastError(), NE_FLM_WRITING_FILE);
|
|
}
|
|
|
|
notifyComplete( rc);
|
|
}
|
|
#endif
|
|
|
|
#if defined( FLM_LINUX) || defined( FLM_SOLARIS)
|
|
if( m_aio.aio_fildes != -1)
|
|
{
|
|
const struct aiocb * pAio = &m_aio;
|
|
|
|
if( aio_suspend( &pAio, 1, NULL) == -1)
|
|
{
|
|
rc = f_mapPlatformError( errno, NE_FLM_MEM);
|
|
}
|
|
|
|
notifyComplete( rc);
|
|
}
|
|
#endif
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
void FLMAPI F_IOBuffer::startTimer(
|
|
void * pStats)
|
|
{
|
|
if ((m_pStats = pStats) != NULL)
|
|
{
|
|
m_ui64ElapMilli = 0;
|
|
f_timeGetTimeStamp( &m_StartTime);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
FLMUINT64 FLMAPI F_IOBuffer::getElapTime( void)
|
|
{
|
|
return( m_ui64ElapMilli);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
void * FLMAPI F_IOBuffer::getStats( void)
|
|
{
|
|
return( m_pStats);
|
|
}
|