//------------------------------------------------------------------------------ // 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); }