diff --git a/flaim/src/f_nici.cpp b/flaim/src/f_nici.cpp index d7e2cb2..4a55d1d 100644 --- a/flaim/src/f_nici.cpp +++ b/flaim/src/f_nici.cpp @@ -2721,52 +2721,31 @@ FSTATIC void GetIV( /***************************************************************************** Desc: Add a globally shared reference to this object. *****************************************************************************/ -FLMUINT F_CCS::AddRef() +FLMINT F_CCS::AddRef() { - FLMUINT uiRefCnt = 0; - -#ifdef ATOMIC_INCDEC_SUPPORT - uiRefCnt = (FLMUINT)ftkAtomicIncrement( &m_ui32RefCnt); -#else - - f_mutexLock( m_hMutex); - uiRefCnt = ++m_ui32RefCnt; - f_mutexUnlock( m_hMutex); - -#endif - - - return( uiRefCnt); + return( ftkAtomicIncrement( &m_i32RefCnt)); } /***************************************************************************** Desc: Removes a globally shared reference to this object. *****************************************************************************/ -FLMUINT F_CCS::Release() +FLMINT F_CCS::Release() { - FLMUINT uiRefCnt = 0; + FLMINT iRefCnt = 0; -#ifdef ATOMIC_INCDEC_SUPPORT - uiRefCnt = (FLMUINT)ftkAtomicDecrement( &m_ui32RefCnt); -#else + iRefCnt = ftkAtomicDecrement( &m_i32RefCnt); - f_mutexLock( m_hMutex); - uiRefCnt = --m_ui32RefCnt; - f_mutexUnlock( m_hMutex); - -#endif - - - if( !uiRefCnt) + if( !iRefCnt) { delete this; } - return( uiRefCnt); + return( iRefCnt); } - - +/***************************************************************************** +Desc: +*****************************************************************************/ #ifdef FLM_USE_NICI #ifndef FLM_UNIX int CCSX_SetNewIV( diff --git a/flaim/src/f_nici.h b/flaim/src/f_nici.h index e7f77df..a554bdf 100644 --- a/flaim/src/f_nici.h +++ b/flaim/src/f_nici.h @@ -187,9 +187,9 @@ public: return m_uiAlgType; } - FLMUINT AddRef( void); + FLMINT AddRef( void); - FLMUINT Release( void); + FLMINT Release( void); private: diff --git a/flaim/src/ffilehdl.cpp b/flaim/src/ffilehdl.cpp index b90c70a..ac4748c 100644 --- a/flaim/src/ffilehdl.cpp +++ b/flaim/src/ffilehdl.cpp @@ -299,7 +299,7 @@ RCODE F_FileHdlMgr::MakeAvailAndRelease( F_FileHdlImp * pFileHdl) { RCODE rc = FERR_OK; - FLMUINT uiRefCnt; + FLMINT iRefCnt; pFileHdl->SetAvailTime(); pMutexRef->Lock(); @@ -318,8 +318,8 @@ RCODE F_FileHdlMgr::MakeAvailAndRelease( // Release the caller's reference to the file handle - uiRefCnt = pFileHdl->Release(); - flmAssert( uiRefCnt == 1); + iRefCnt = pFileHdl->Release(); + flmAssert( iRefCnt == 1); pMutexRef->Unlock(); return( rc); diff --git a/flaim/src/flaim.h b/flaim/src/flaim.h index 3d87231..78659fa 100644 --- a/flaim/src/flaim.h +++ b/flaim/src/flaim.h @@ -97,7 +97,7 @@ #define FLM_UNIX #define FLM_SOLARIS #define FLM_STRICT_ALIGNMENT - #if defined( sparc) || defined( __sparc) || defined( __sparcv9) + #if defined( sparc) || defined( __sparc) || defined( __sparc__) #define FLM_SPARC #define FLM_BIG_ENDIAN #endif @@ -273,7 +273,11 @@ #define FLMCDECL #define FLMAPI #define FLMCOM - #define FINLINE inline + #if defined( __GNUC__) + #define FINLINE __attribute__((always_inline)) inline + #else + #define FINLINE inline + #endif #else #error Platform not supported #endif @@ -814,7 +818,7 @@ F_Base() { - m_ui32RefCnt = 1; + m_i32RefCnt = 1; } virtual ~F_Base() @@ -824,22 +828,21 @@ /// Increment the reference count for this object. /// The reference count is the number of pointers that are referencing this object. /// Return value is the incremented reference count. - FINLINE FLMUINT AddRef( void) + FINLINE FLMINT AddRef( void) { - m_ui32RefCnt++; - return m_ui32RefCnt; + return( ++m_i32RefCnt); } /// Decrement the reference count for this object. /// The reference count is the number of pointers that are referencing this object. /// Return value is the decremented reference count. If the reference count goes to /// zero, the object will be deleted. - FLMUINT Release( void); + FLMINT Release( void); /// Return the current reference count on the object. - FINLINE FLMUINT getRefCount( void) + FINLINE FLMINT getRefCount( void) { - return( m_ui32RefCnt); + return( m_i32RefCnt); } /// Overloaded new operator for objects of this class. @@ -916,7 +919,7 @@ protected: - FLMUINT32 m_ui32RefCnt; + FLMINT32 m_i32RefCnt; friend class F_FileHdlPage; friend class F_FileHdlMgrPage; @@ -4603,16 +4606,13 @@ /// Increment the reference count for this ::FlmRecord object. /// The reference count is the number of pointers that are referencing this ::FlmRecord object. /// Return value is the incremented reference count. - FINLINE FLMUINT AddRef( void) - { - return( AddRef( FALSE)); - } + FLMINT AddRef( void); /// Decrement the reference count for this ::FlmRecord object. /// The reference count is the number of pointers that are referencing this ::FlmRecord object. /// Return value is the decremented reference count. If the reference count goes to /// zero, the ::FlmRecord object will be deleted. - FINLINE FLMUINT Release( void) + FINLINE FLMINT Release( void) { return( Release( FALSE)); } @@ -5332,10 +5332,7 @@ private: - FLMUINT AddRef( - FLMBOOL bMutexLocked); - - FLMUINT Release( + FLMINT Release( FLMBOOL bMutexLocked); void * parent( diff --git a/flaim/src/flalloc.cpp b/flaim/src/flalloc.cpp index 438fec3..9ed1678 100644 --- a/flaim/src/flalloc.cpp +++ b/flaim/src/flalloc.cpp @@ -1,1464 +1,1464 @@ -//------------------------------------------------------------------------- -// Desc: Memory allocation routines. -// Tabs: 3 -// -// Copyright (c) 1991,1993,1995-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: flalloc.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ -//------------------------------------------------------------------------- - -#include "flaimsys.h" - -#undef f_free -#undef f_alloc -#undef f_calloc -#undef f_realloc -#undef f_recalloc - -#ifdef FLM_NLM - extern "C" - { - extern LONG gv_lAllocRTag; - } -#endif - -#ifdef FLM_UNIX - #ifdef HAVE_CONFIG_H - #include "config.h" - #endif - - #ifdef HAVE_DLADDR - #include - #endif - #include - -#endif - -#define F_GET_ALLOC_PTR( pDataPtr) \ - (FLMBYTE *)((FLMBYTE *)(pDataPtr) - sizeof( F_MEM_HDR)) - -#define F_GET_DATA_PTR( pAllocPtr) \ - (FLMBYTE *)((FLMBYTE *)(pAllocPtr) + sizeof( F_MEM_HDR)) - -#define F_GET_MEM_DATA_SIZE( pDataPtr) \ - (((F_MEM_HDR *)(F_GET_ALLOC_PTR( pDataPtr)))->uiDataSize) - -// Picket fence - -#define F_PICKET_FENCE "FFFFFFFF" - -#if defined( FLM_DEBUG) - #define F_PICKET_FENCE_SIZE 8 -#else - #define F_PICKET_FENCE_SIZE 0 -#endif - -#define MEM_PTR_INIT_ARRAY_SIZE 512 -#define MEM_MAX_STACK_WALK_DEPTH 32 - -// If stack tracking is on, leak checking also -// needs to be on. - -#ifndef FLM_DEBUG - #ifdef DEBUG_SIM_OUT_OF_MEM - #undef DEBUG_SIM_OUT_OF_MEM - #endif -#endif - -#ifdef FLM_DEBUG - -// Local function prototypes - -FSTATIC FLMBOOL initMemTracking( - void); - -FSTATIC void saveMemTrackingInfo( - F_MEM_HDR * pHdr); - -FSTATIC void updateMemTrackingInfo( - F_MEM_HDR * pHdr); - -FSTATIC void freeMemTrackingInfo( - FLMBOOL bMutexAlreadyLocked, - FLMUINT uiId, - FLMUINT * puiStack); - -#ifdef DEBUG_SIM_OUT_OF_MEM - -//one of every OUT_OF_MEM_FREQUENCY calls will fail - -#define OUT_OF_MEM_FREQUENCY 40000 - -//OUT_OF_MEM_SEQUENCE_LENGTH calls in a row will fail - -#define OUT_OF_MEM_SEQUENCE_LENGTH 10 - -FLMBOOL SimulateOutOfMemory() -{ - if ( - //is the flag turned on - (gv_FlmSysData.uiOutOfMemSimEnabledFlag == - (FLMUINT)OUT_OF_MEM_SIM_ENABLED_FLAG) && - - //continuing a sequence of failures - ((gv_FlmSysData.uiSimOutOfMemFailSequence > 0) || - - //failing randomly for the first time, and starting a new sequence - (f_randomChoice( &gv_FlmSysData.memSimRandomGen, 0, OUT_OF_MEM_FREQUENCY) == 0))) - { - gv_FlmSysData.uiSimOutOfMemFailTotal++; - gv_FlmSysData.uiSimOutOfMemFailSequence++; - //if reached the end of failure sequence, reset back to 0 so the - //sequence will cease - if ( gv_FlmSysData.uiSimOutOfMemFailSequence >= OUT_OF_MEM_SEQUENCE_LENGTH) - { - gv_FlmSysData.uiSimOutOfMemFailSequence = 0; - } - return TRUE; - } - else - { - return FALSE; - } -} - -#endif //#ifdef DEBUG_SIM_OUT_OF_MEM - -#if defined( FLM_NLM) - - void * memGetEBP(void); - -#ifdef __MWERKS__ - - void * memGetEBP(void) - { - __asm - { - mov eax,[ebp] - } - } - -#else - - #pragma aux memGetEBP = "mov eax,ebp"; - -#endif - -void * memValueAtStackOffset( - void * pos, - int offset); - -#ifdef __MWERKS__ - - void * memValueAtStackOffset( void *, int) - { - __asm - { - mov eax,[ebp+0x8] - mov ebx,[ebp+0xC] - mov eax,ss:[eax+ebx] - } - } - -#else - - #pragma aux memValueAtStackOffset = "mov eax,ss:[eax+ebx]" parm [eax] [ebx]; - -#endif - -/**************************************************************************** -Desc: -****************************************************************************/ -FLMUINT * memWalkStack( void) -{ - FLMUINT uiLoop; - FLMUINT uiRtnAddr; - FLMUINT uiEbp = (FLMUINT) memGetEBP(); - FLMUINT uiAddresses [MEM_MAX_STACK_WALK_DEPTH + 1]; - FLMUINT * puiAddresses; - - uiEbp = (FLMUINT) memValueAtStackOffset( (void *)uiEbp, 0); - uiRtnAddr = (FLMUINT) memValueAtStackOffset( (void *)uiEbp, 4); - - for( uiLoop = 0; uiLoop < MEM_MAX_STACK_WALK_DEPTH; uiLoop++) - { - FLMUINT uiOldEbp; - - uiAddresses [uiLoop] = uiRtnAddr; - - if( !uiEbp) - { - break; - } - - uiOldEbp = uiEbp; - uiEbp = (FLMUINT) memValueAtStackOffset( (void *)uiEbp, 0); - - if (!uiEbp || uiEbp <= uiOldEbp || uiEbp > uiOldEbp + 5000) - { - break; - } - - uiRtnAddr = (FLMUINT) memValueAtStackOffset( (void *) uiEbp, 4); - } - - uiAddresses[ uiLoop] = 0; - - if( (puiAddresses = (FLMUINT *)os_malloc( - sizeof( FLMUINT) * (uiLoop+1))) != NULL) - { - f_memcpy( puiAddresses, &uiAddresses [0], - sizeof( FLMUINT) * (uiLoop + 1)); - } - - return( puiAddresses); -} - -#elif defined( FLM_WIN) - -/******************************************************************** -Desc: Reads NSIZE bytes of memory from LPBASEADDRESS -*********************************************************************/ -static BOOL CALLBACK ReadProcMemory( - HANDLE, - DWORD64 lpBaseAddress, - PVOID lpBuffer, - DWORD nSize, - LPDWORD lpNumberOfBytesRead) -{ - static HANDLE hRealProcess = GetCurrentProcess(); - SIZE_T bytesRead = 0; - BOOL rv = ReadProcessMemory(hRealProcess, - (const void *)((FLMUINT)lpBaseAddress & 0xFFFFFFFF), - lpBuffer, SIZE_T(nSize), &bytesRead); - - if (lpNumberOfBytesRead) - { - *lpNumberOfBytesRead = DWORD(bytesRead & 0xFFFFFFFF); - } - - return( rv); -} - -/******************************************************************** -Desc: Walk the call stack. -*********************************************************************/ -FLMUINT * memWalkStack() -{ - STACKFRAME64 stackFrame; - DWORD machineType; - FLMUINT uiLoop; - FLMUINT uiAddresses [MEM_MAX_STACK_WALK_DEPTH + 1]; - FLMUINT * puiAddresses; - HANDLE hProcess = GetCurrentProcess(); - FLMUINT uiAddrCount; - const void * pc; - const void * fp; - - __asm - { - call $ + 5 - pop eax - mov [pc], eax - mov [fp], ebp - } - -#ifdef FLM_64BIT - machineType = IMAGE_FILE_MACHINE_IA64; -#else - machineType = IMAGE_FILE_MACHINE_I386; -#endif - - f_memset( &stackFrame, 0, sizeof( stackFrame)); - - stackFrame.AddrPC.Offset = DWORD((FLMUINT)pc); - stackFrame.AddrPC.Mode = AddrModeFlat; - stackFrame.AddrFrame.Offset = DWORD((FLMUINT)fp); - stackFrame.AddrFrame.Mode = AddrModeFlat; - - f_mutexLock( gv_FlmSysData.hMemTrackingMutex); - - // We have already processed the address inside memWalkStack - - uiAddrCount = 1; - uiLoop = 0; - for (;;) - { - if( !StackWalk64( machineType, hProcess, 0, &stackFrame, - 0, ReadProcMemory, SymFunctionTableAccess64, SymGetModuleBase64, 0)) - { - break; - } - - if( !stackFrame.AddrFrame.Offset) - { - break; - } - - // Skip the first two addresses. These represent the following: - // 1) memWalkStack - // 2) saveMemTrackingInfo or updateMemTrackingInfo - // We don't need to see them in the stack trace. - - uiAddrCount++; - if (uiAddrCount > 2) - { - uiAddresses [uiLoop] = (FLMUINT)stackFrame.AddrReturn.Offset; - uiLoop++; - if (uiLoop == MEM_MAX_STACK_WALK_DEPTH) - { - break; - } - } - } - - f_mutexUnlock( gv_FlmSysData.hMemTrackingMutex); - - uiAddresses [uiLoop] = 0; - if ((puiAddresses = (FLMUINT *)os_malloc( - sizeof( FLMUINT) * (uiLoop+1))) != NULL) - { - f_memcpy( puiAddresses, &uiAddresses [0], sizeof( FLMUINT) * (uiLoop + 1)); - } - return( puiAddresses); -} -#else -FLMUINT * memWalkStack() -{ - return( NULL); -} -#endif - -/******************************************************************** -Desc: Initialize memory tracking -*********************************************************************/ -FSTATIC FLMBOOL initMemTracking( void) -{ - RCODE rc; - F_MUTEX memMutex; - - if (!gv_FlmSysData.bMemTrackingInitialized && !gv_FlmSysData.uiInitThreadId) - { - gv_FlmSysData.uiInitThreadId = f_threadId(); - rc = f_mutexCreate( &memMutex); - f_sleep( 50); - - // Only set to initialized if we were the last thread - // to set gv_FlmSysData.uiInitThreadId - - if (f_threadId() == gv_FlmSysData.uiInitThreadId) - { - if (RC_OK( rc)) - { - gv_FlmSysData.hMemTrackingMutex = memMutex; - } - else - { - gv_FlmSysData.hMemTrackingMutex = F_MUTEX_NULL; - } -#ifdef FLM_WIN - SymSetOptions( SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS); - gv_FlmSysData.hMemProcess = GetCurrentProcess(); - SymInitialize( gv_FlmSysData.hMemProcess, NULL, TRUE); -#endif - gv_FlmSysData.bMemTrackingInitialized = TRUE; - } - else - { - if (RC_OK( rc)) - { - f_mutexDestroy( &memMutex); - } - } - } - - // Go into a loop until we see initialized flag set to TRUE - // Could be another thread that is doing it. - - while (!gv_FlmSysData.bMemTrackingInitialized) - { - f_sleep( 10); - } - return( (gv_FlmSysData.hMemTrackingMutex != F_MUTEX_NULL) ? TRUE : FALSE); -} - -/******************************************************************** -Desc: Save memory tracking information - called on alloc or realloc. -*********************************************************************/ -FSTATIC void saveMemTrackingInfo( - F_MEM_HDR * pHdr) -{ - FLMUINT uiNewCnt; - FLMUINT uiId; - void ** pNew; - - if (gv_FlmSysData.bTrackLeaks && initMemTracking()) - { - f_mutexLock( gv_FlmSysData.hMemTrackingMutex); - - // See if there is enough room in the array - - if (gv_FlmSysData.uiMemNumPtrs == gv_FlmSysData.uiMemTrackingPtrArraySize) - { - - // If array is not initialized, use initial count. Otherwise - // double the size. - - uiNewCnt = (FLMUINT)((!gv_FlmSysData.uiMemTrackingPtrArraySize) - ? MEM_PTR_INIT_ARRAY_SIZE - : gv_FlmSysData.uiMemTrackingPtrArraySize * 2); - if ((pNew = (void **)os_malloc( sizeof( void *) * uiNewCnt)) != NULL) - { - - // Copy the pointers from the old array, if any, - // into the newly allocated array. - - if (gv_FlmSysData.uiMemTrackingPtrArraySize) - { - f_memcpy( pNew, gv_FlmSysData.ppvMemTrackingPtrs, - sizeof( void *) * gv_FlmSysData.uiMemTrackingPtrArraySize); - os_free( gv_FlmSysData.ppvMemTrackingPtrs); - } - f_memset( &pNew [gv_FlmSysData.uiMemTrackingPtrArraySize], 0, - sizeof( void *) * (uiNewCnt - gv_FlmSysData.uiMemTrackingPtrArraySize)); - gv_FlmSysData.ppvMemTrackingPtrs = pNew; - gv_FlmSysData.uiMemTrackingPtrArraySize = uiNewCnt; - } - } - - // If we are still full, we were not able to reallocate memory, so we - // do nothing. - - if (gv_FlmSysData.uiMemNumPtrs == gv_FlmSysData.uiMemTrackingPtrArraySize) - { - pHdr->uiAllocationId = 0; - } - else - { - // Find an empty slot - there has to be one! - - uiId = gv_FlmSysData.uiMemNextPtrSlotToUse; - while (gv_FlmSysData.ppvMemTrackingPtrs [uiId]) - { - if (++uiId == gv_FlmSysData.uiMemTrackingPtrArraySize) - { - uiId = 0; - } - } - - // Allocation ID in the header is offset by one to avoid - // using a value of zero. - - pHdr->uiAllocationId = uiId + 1; - gv_FlmSysData.ppvMemTrackingPtrs [uiId] = pHdr; - gv_FlmSysData.uiMemNumPtrs++; - if ((gv_FlmSysData.uiMemNextPtrSlotToUse = uiId + 1) == - gv_FlmSysData.uiMemTrackingPtrArraySize) - { - gv_FlmSysData.uiMemNextPtrSlotToUse = 0; - } - } - pHdr->uiAllocCnt = ++gv_FlmSysData.uiAllocCnt; - f_mutexUnlock( gv_FlmSysData.hMemTrackingMutex); - } - else - { - pHdr->uiAllocationId = 0; - pHdr->uiAllocCnt = 0; - } - - // Follow the stack. - - if (gv_FlmSysData.bTrackLeaks && gv_FlmSysData.bStackWalk) - { - pHdr->puiStack = memWalkStack(); - } - else - { - pHdr->puiStack = NULL; - } -} - -/******************************************************************** -Desc: Update memory tracking information - called after realloc -*********************************************************************/ -FSTATIC void updateMemTrackingInfo( - F_MEM_HDR * pHdr) -{ - if (pHdr->puiStack) - { - os_free( pHdr->puiStack); - pHdr->puiStack = NULL; - } - if (gv_FlmSysData.bTrackLeaks && gv_FlmSysData.bStackWalk) - { - pHdr->puiStack = memWalkStack(); - } -} - -/******************************************************************** -Desc: Free memory tracking information - called on free. -*********************************************************************/ -FSTATIC void freeMemTrackingInfo( - FLMBOOL bMutexAlreadyLocked, - FLMUINT uiId, - FLMUINT * puiStack - ) -{ - if (uiId) - { - // NOTE: If uiId is non-zero, it means we had to have - // successfully initialized, so we are guaranteed to - // have a mutex. - - if ( !bMutexAlreadyLocked) - { - f_mutexLock( gv_FlmSysData.hMemTrackingMutex); - } - - // Allocation ID in the header is offset by one so that it - // is never zero - a value of zero means that the allocation - // does not have a slot for tracking it in the array. - - gv_FlmSysData.ppvMemTrackingPtrs [uiId - 1] = NULL; - flmAssert( gv_FlmSysData.uiMemNumPtrs); - gv_FlmSysData.uiMemNumPtrs--; - - if ( !bMutexAlreadyLocked) - { - f_mutexUnlock( gv_FlmSysData.hMemTrackingMutex); - } - } - - // Free the stack information, if any. - - if (puiStack) - { - os_free( puiStack); - } -} - -/******************************************************************** -Desc: Log memory leaks. -*********************************************************************/ -void logMemLeak( - F_MEM_HDR * pHdr) -{ - char szMessageBuffer [1024]; - char * pszTmp = &szMessageBuffer [0]; - F_FileHdl * pFileHdl = NULL; - FLMBOOL bOldTrackLeaks = gv_FlmSysData.bTrackLeaks; - - gv_FlmSysData.bTrackLeaks = FALSE; // This ensures that any future - // allocations (for instance, - // allocating the file handle for the - // memtest.ert file) will not try to - // lock the mem tracking mutex. - - - - // Format message to be logged. - - f_strcpy( pszTmp, "Abort=Debug, Retry=Continue, Ignore=Don't Show\r\n"); - while (*pszTmp) - { - pszTmp++; - } -#if defined( FLM_64BIT) - f_sprintf( pszTmp, "Unfreed Pointer: 0x%016I64x\r\n", (FLMUINT)(&pHdr [1])); -#else - f_sprintf( pszTmp, "Unfreed Pointer: 0x%08x\r\n", - (unsigned)((FLMUINT)(&pHdr [1]))); -#endif - while (*pszTmp) - { - pszTmp++; - } - - if (pHdr->pszFileName) - { - f_sprintf( pszTmp, "Source: %s, Line#: %u\r\n", pHdr->pszFileName, - (unsigned)pHdr->iLineNumber); - while (*pszTmp) - { - pszTmp++; - } - } - - if (pHdr->uiAllocCnt) - { - f_sprintf( pszTmp, "Malloc #: %u\r\n", (unsigned)pHdr->uiAllocCnt); - while (*pszTmp) - { - pszTmp++; - } - } - f_sprintf( pszTmp, "Size: %u bytes\r\n", (unsigned)pHdr->uiDataSize); - while (*pszTmp) - { - pszTmp++; - } - - if (pHdr->puiStack) - { - FLMUINT * puiStack = pHdr->puiStack; - FLMUINT uiLen = pszTmp - szMessageBuffer; - char szFuncName [200]; - char * pszFuncName; -#ifdef FLM_WIN - IMAGEHLP_SYMBOL * pImgHlpSymbol; - - pImgHlpSymbol = (IMAGEHLP_SYMBOL *)os_malloc( - sizeof( IMAGEHLP_SYMBOL) + 100); -#endif - - while (*puiStack) - { - szFuncName [0] = 0; -#if defined( FLM_WIN) - if (pImgHlpSymbol) - { -#ifdef FLM_64BIT - DWORD64 udDisplacement; -#else - DWORD udDisplacement; -#endif - - pImgHlpSymbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL); - pImgHlpSymbol->Address = *puiStack; - pImgHlpSymbol->MaxNameLength = 100; - - if (SymGetSymFromAddr( gv_FlmSysData.hMemProcess, *puiStack, - &udDisplacement, pImgHlpSymbol)) - { - f_sprintf( szFuncName, "\t%s + %X\r\n", - (&pImgHlpSymbol->Name [0]), - udDisplacement); - } - } -#elif defined( FLM_NLM) - { - szFuncName [0] = '\t'; - GetClosestSymbol( (BYTE *)(&szFuncName[1]), (LONG)(*puiStack)); - } -#else - -#ifdef HAVE_DLADDR - { - Dl_info dlip; - - if (dladdr( (void *)(*puiStack), &dlip) != 0 && dlip.dli_sname) - { - const char * pszFileName; - if (dlip.dli_saddr != (void *)(*puiStack)) - { - pszFileName = strrchr(dlip.dli_fname, '/'); - if (!pszFileName) - { - pszFileName = dlip.dli_fname; - } - else - { - pszFileName++; // skip over slash - } - f_sprintf( szFuncName, "\t0x%08x (%s)\r\n", - (unsigned)(*puiStack), pszFileName); - } - else - { - f_sprintf( szFuncName, "\t%s\r\n", dlip.dli_sname); - } - } - } -#endif - -#endif - - // If szFuncName [0] is zero, we didn't find a name, so we - // just output the address in HEX. - - if (!szFuncName [0]) - { - f_sprintf( szFuncName, "\t0x%08X\r\n", (unsigned)*puiStack ); - } - - // Output whatever portion of the name will fit into the - // message buffer. - - pszFuncName = &szFuncName [0]; - while (*pszFuncName && uiLen < sizeof( szMessageBuffer) - 1) - { - *pszTmp++ = *pszFuncName++; - uiLen++; - } - - // Process next address in the stack. - - puiStack++; - } - *pszTmp = 0; -#ifdef FLM_WIN - if (pImgHlpSymbol) - { - os_free( pImgHlpSymbol); - } -#endif - } - -#ifdef FLM_WIN - FLMINT iRet; - - iRet = MessageBox( NULL, (LPCTSTR)szMessageBuffer, "WIN Memory Testing", - MB_ABORTRETRYIGNORE | MB_ICONINFORMATION | MB_TASKMODAL - | MB_SETFOREGROUND | MB_DEFBUTTON2); - if (iRet == IDIGNORE) - { - gv_FlmSysData.bLogLeaks = TRUE; - } - else if (iRet == IDABORT) - { - flmAssert( 0); - } -#else - gv_FlmSysData.bLogLeaks = TRUE; -#endif - - if (gv_FlmSysData.bLogLeaks) - { - F_FileSystemImp FileSystem; - RCODE rc; - FLMUINT uiDummy; -#ifdef FLM_NLM - const char * pszErrPath = "sys:\\memtest.ert"; -#else - const char * pszErrPath = "memtest.ert"; -#endif - - if (RC_BAD( rc = FileSystem.Open( pszErrPath, - F_IO_RDWR | F_IO_SH_DENYNONE, &pFileHdl))) - { - if (rc == FERR_IO_PATH_NOT_FOUND) - { - rc = FileSystem.Create( pszErrPath, - F_IO_RDWR | F_IO_SH_DENYNONE, &pFileHdl); - } - } - else - { - FLMUINT uiOffset; - - // Position to append to file. - - rc = pFileHdl->Seek( 0, F_IO_SEEK_END, &uiOffset); - } - - // If we successfully opened the file, write to it. - - if (RC_OK( rc)) - { - if (RC_OK( pFileHdl->Write( F_IO_CURRENT_POS, - (FLMUINT)(pszTmp - &szMessageBuffer [0]), - szMessageBuffer, &uiDummy))) - { - (void)pFileHdl->Flush(); - } - pFileHdl->Close(); - } - } -//Exit: - - gv_FlmSysData.bTrackLeaks = bOldTrackLeaks; - - if (pFileHdl) - { - pFileHdl->Release(); - } -} -#endif - -/******************************************************************** -Desc: Initialize memory - if not already done. -*********************************************************************/ -void f_memoryInit( void) -{ -#ifdef FLM_DEBUG - (void)initMemTracking(); -#endif -} - -/******************************************************************** -Desc: Clean up memory and check for unfreed memory. -*********************************************************************/ -void f_memoryCleanup( void) -{ - -#ifdef FLM_DEBUG - if (initMemTracking()) - { - FLMUINT uiId; - F_MEM_HDR * pHdr; - - f_mutexLock( gv_FlmSysData.hMemTrackingMutex); - for (uiId = 0; uiId < gv_FlmSysData.uiMemTrackingPtrArraySize; uiId++) - { - if ((pHdr = (F_MEM_HDR *)gv_FlmSysData.ppvMemTrackingPtrs [uiId]) != NULL) - { - logMemLeak( pHdr); - freeMemTrackingInfo( TRUE, uiId + 1, pHdr->puiStack); - } - } - - // Free the memory pointer array. - - os_free( gv_FlmSysData.ppvMemTrackingPtrs); - gv_FlmSysData.ppvMemTrackingPtrs = NULL; - gv_FlmSysData.uiMemTrackingPtrArraySize = 0; - gv_FlmSysData.uiMemNumPtrs = 0; - - f_mutexUnlock( gv_FlmSysData.hMemTrackingMutex); - - // Free up the mutex. - - f_mutexDestroy( &gv_FlmSysData.hMemTrackingMutex); - - // Reset to unitialized state. - - gv_FlmSysData.uiInitThreadId = 0; - gv_FlmSysData.hMemTrackingMutex = F_MUTEX_NULL; - gv_FlmSysData.bMemTrackingInitialized = FALSE; -#ifdef FLM_WIN - SymCleanup( gv_FlmSysData.hMemProcess); -#endif - } -#endif -} - -/******************************************************************** -Desc: Allocate Memory. -*********************************************************************/ -RCODE f_alloc( - FLMUINT uiSize, - void ** ppvPtr, - const char * pszFileName, - int iLineNumber) -{ - RCODE rc = FERR_OK; - F_MEM_HDR * pHdr; - -#ifndef FLM_DEBUG - F_UNREFERENCED_PARM( pszFileName); - F_UNREFERENCED_PARM( iLineNumber); -#endif - -#ifdef DEBUG_SIM_OUT_OF_MEM - if ( SimulateOutOfMemory()) - { - *ppvPtr = NULL; - rc = RC_SET( FERR_MEM); - goto Exit; - } -#endif - - if ((pHdr = (F_MEM_HDR *)os_malloc( uiSize + - sizeof( F_MEM_HDR) + - F_PICKET_FENCE_SIZE)) == NULL) - { - rc = RC_SET( FERR_MEM); - goto Exit; - } - - pHdr->uiDataSize = uiSize; - *ppvPtr = (void *)(&pHdr [1]); - -#ifdef FLM_DEBUG - pHdr->iLineNumber = iLineNumber; - pHdr->pszFileName = pszFileName; - saveMemTrackingInfo( pHdr); - - #if F_PICKET_FENCE_SIZE - - f_memcpy( ((FLMBYTE *)(*ppvPtr)) + uiSize, - F_PICKET_FENCE, F_PICKET_FENCE_SIZE); - - #endif -#endif - -Exit: - - return( rc); -} - -/******************************************************************** -Desc: Allocate and initialize memory. -*********************************************************************/ -RCODE f_calloc( - FLMUINT uiSize, - void ** ppvPtr, - const char * pszFileName, - int iLineNumber) -{ - RCODE rc = FERR_OK; - F_MEM_HDR * pHdr; - -#ifndef FLM_DEBUG - F_UNREFERENCED_PARM( pszFileName); - F_UNREFERENCED_PARM( iLineNumber); -#endif - -#ifdef DEBUG_SIM_OUT_OF_MEM - if ( SimulateOutOfMemory()) - { - *ppvPtr = NULL; - rc = RC_SET( FERR_MEM); - goto Exit; - } -#endif - - if ((pHdr = (F_MEM_HDR *)os_malloc( uiSize + - sizeof( F_MEM_HDR) + - F_PICKET_FENCE_SIZE)) == NULL) - { - rc = RC_SET( FERR_MEM); - goto Exit; - } - - pHdr->uiDataSize = uiSize; - *ppvPtr = (void *)(&pHdr [1]); - f_memset( *ppvPtr, 0, uiSize); -#ifdef FLM_DEBUG - pHdr->iLineNumber = iLineNumber; - pHdr->pszFileName = pszFileName; - saveMemTrackingInfo( pHdr); - - #if F_PICKET_FENCE_SIZE - - f_memcpy( ((FLMBYTE *)(*ppvPtr)) + uiSize, - F_PICKET_FENCE, F_PICKET_FENCE_SIZE); - - #endif - -#endif -Exit: - return( rc); -} - -/******************************************************************** -Desc: Reallocate memory. -*********************************************************************/ -RCODE f_realloc( - FLMUINT uiSize, - void ** ppvPtr, - const char * pszFileName, - int iLineNumber) -{ - RCODE rc = FERR_OK; - F_MEM_HDR * pNewHdr; -#ifdef FLM_DEBUG - F_MEM_HDR * pOldHdr; - FLMUINT uiOldAllocationId; - FLMUINT * puiOldStack; -#endif - -#ifdef DEBUG_SIM_OUT_OF_MEM - if ( SimulateOutOfMemory()) - { - *ppvPtr = NULL; - rc = RC_SET( FERR_MEM); - goto Exit; - } -#endif - - if (!(*ppvPtr)) - { - rc = f_alloc( uiSize, ppvPtr, pszFileName, iLineNumber); - goto Exit; - } - -#ifdef FLM_DEBUG - pOldHdr = (F_MEM_HDR *)F_GET_ALLOC_PTR( *ppvPtr); - - #if F_PICKET_FENCE_SIZE - - // Verify the old picket fence - - if (f_memcmp( ((FLMBYTE *)(*ppvPtr)) + pOldHdr->uiDataSize, - F_PICKET_FENCE, F_PICKET_FENCE_SIZE) != 0) - { - flmAssert( 0); - } - - #endif - - uiOldAllocationId = pOldHdr->uiAllocationId; - puiOldStack = pOldHdr->puiStack; -#endif - - if ((pNewHdr = (F_MEM_HDR *)os_realloc( F_GET_ALLOC_PTR( *ppvPtr), - uiSize + sizeof( F_MEM_HDR) + - F_PICKET_FENCE_SIZE)) == NULL) - { - rc = RC_SET( FERR_MEM); - goto Exit; - } - - pNewHdr->uiDataSize = uiSize; - *ppvPtr = (void *)(&pNewHdr [1]); -#ifdef FLM_DEBUG - pNewHdr->iLineNumber = iLineNumber; - pNewHdr->pszFileName = pszFileName; - if (pNewHdr != pOldHdr) - { - freeMemTrackingInfo( FALSE, uiOldAllocationId, puiOldStack); - saveMemTrackingInfo( pNewHdr); - } - else - { - updateMemTrackingInfo( pNewHdr); - } - - #if F_PICKET_FENCE_SIZE - - f_memcpy( ((FLMBYTE *)(*ppvPtr)) + uiSize, - F_PICKET_FENCE, F_PICKET_FENCE_SIZE); - - #endif - -#endif - -Exit: - return( rc); -} - -/******************************************************************** -Desc: Reallocate memory, and initialize the new part. -*********************************************************************/ -RCODE f_recalloc( - FLMUINT uiSize, - void ** ppvPtr, - const char * pszFileName, - int iLineNumber) -{ - RCODE rc = FERR_OK; - F_MEM_HDR * pNewHdr; - FLMUINT uiOldSize; -#ifdef FLM_DEBUG - F_MEM_HDR * pOldHdr; - FLMUINT uiOldAllocationId; - FLMUINT * puiOldStack; -#endif - -#ifdef DEBUG_SIM_OUT_OF_MEM - if ( SimulateOutOfMemory()) - { - *ppvPtr = NULL; - rc = RC_SET( FERR_MEM); - goto Exit; - } -#endif - - if (!(*ppvPtr)) - { - rc = f_calloc( uiSize, ppvPtr, pszFileName, iLineNumber); - goto Exit; - } - -#ifdef FLM_DEBUG - pOldHdr = (F_MEM_HDR *)F_GET_ALLOC_PTR( *ppvPtr); - - #if F_PICKET_FENCE_SIZE - - // Verify the old picket fence - - if (f_memcmp( ((FLMBYTE *)(*ppvPtr)) + pOldHdr->uiDataSize, - F_PICKET_FENCE, F_PICKET_FENCE_SIZE) != 0) - { - flmAssert( 0); - } - - #endif - - uiOldAllocationId = pOldHdr->uiAllocationId; - puiOldStack = pOldHdr->puiStack; - -#endif - - uiOldSize = F_GET_MEM_DATA_SIZE( *ppvPtr); - - if ((pNewHdr = (F_MEM_HDR *)os_realloc( F_GET_ALLOC_PTR( *ppvPtr), - uiSize + sizeof( F_MEM_HDR) + - F_PICKET_FENCE_SIZE)) == NULL) - { - rc = RC_SET( FERR_MEM); - goto Exit; - } - - pNewHdr->uiDataSize = uiSize; - *ppvPtr = (void *)(&pNewHdr [1]); - if (uiOldSize < uiSize) - { - f_memset( ((FLMBYTE *)(*ppvPtr)) + uiOldSize, 0, - uiSize - uiOldSize); - } -#ifdef FLM_DEBUG - pNewHdr->iLineNumber = iLineNumber; - pNewHdr->pszFileName = pszFileName; - if (pNewHdr != pOldHdr) - { - freeMemTrackingInfo( FALSE, uiOldAllocationId, puiOldStack); - saveMemTrackingInfo( pNewHdr); - } - else - { - updateMemTrackingInfo( pNewHdr); - } - - #if F_PICKET_FENCE_SIZE - - f_memcpy( ((FLMBYTE *)(*ppvPtr)) + uiSize, - F_PICKET_FENCE, F_PICKET_FENCE_SIZE); - - #endif - -#endif -Exit: - return( rc); -} - -/******************************************************************** -Desc: Free previously allocated memory. -*********************************************************************/ -void f_free( - void ** ppvPtr) -{ - if (*ppvPtr) - { - -#ifdef FLM_DEBUG - F_MEM_HDR * pHdr = (F_MEM_HDR *)F_GET_ALLOC_PTR( *ppvPtr); - - #if F_PICKET_FENCE_SIZE - - // Check the picket fence - - if (f_memcmp( ((FLMBYTE *)(*ppvPtr)) + pHdr->uiDataSize, - F_PICKET_FENCE, F_PICKET_FENCE_SIZE) != 0) - { - flmAssert( 0); - } - - #endif - - freeMemTrackingInfo( FALSE, pHdr->uiAllocationId, pHdr->puiStack); -#endif - - os_free( F_GET_ALLOC_PTR( *ppvPtr)); - *ppvPtr = NULL; - } -} - -/**************************************************************************** -Desc: -****************************************************************************/ -#ifdef FLM_NLM -void * nlm_realloc( - void * pMemory, - size_t newSize) -{ - void * pNewMemory; - LONG lSize; - - if( !pMemory) - { - pNewMemory = Alloc( newSize, gv_lAllocRTag); - goto Exit; - } - - lSize = SizeOfAllocBlock( pMemory); - - pNewMemory = os_malloc( newSize); - if( !pNewMemory) - { - goto Exit; - } - - if( lSize > newSize) - { - lSize = newSize; - } - - f_memcpy( pNewMemory, pMemory, lSize); - - if( pMemory) - { - Free( pMemory); - } - -Exit: - - return( pNewMemory); -} -#endif - -#undef new -#undef delete -#define FLM_NEW_MEMORY_SIGNATURE 0xABCDABCD - -/**************************************************************************** -Desc: -****************************************************************************/ -void * F_Base::operator new( - FLMSIZET uiSize) -#ifndef FLM_NLM - throw() -#endif -{ - void * pvReturnPtr = NULL; - - uiSize += FLM_ALIGN_SIZE; - f_alloc( uiSize, &pvReturnPtr, "unknown", 0); - - if( pvReturnPtr) - { - *((FLMUINT *)pvReturnPtr) = FLM_NEW_MEMORY_SIGNATURE; - pvReturnPtr = (void *)(((FLMBYTE *)pvReturnPtr) + FLM_ALIGN_SIZE); - } - - return( pvReturnPtr); -} - -/**************************************************************************** -Desc: -****************************************************************************/ -void * F_Base::operator new[]( - FLMSIZET uiSize) -#ifndef FLM_NLM - throw() -#endif -{ - void * pvReturnPtr = NULL; - - uiSize += FLM_ALIGN_SIZE; - f_alloc( uiSize, &pvReturnPtr, "unknown", 0); - - if( pvReturnPtr) - { - *((FLMUINT *)pvReturnPtr) = FLM_NEW_MEMORY_SIGNATURE; - pvReturnPtr = (void *)(((FLMBYTE *)pvReturnPtr) + FLM_ALIGN_SIZE); - } - - return( pvReturnPtr); -} - -/**************************************************************************** -Desc: -****************************************************************************/ -#ifdef FLM_DEBUG -void * F_Base::operator new( - FLMSIZET uiSize, - const char * pszFile, - int iLine) -#ifndef FLM_NLM - throw() -#endif -{ - void * pvReturnPtr = NULL; - - uiSize += FLM_ALIGN_SIZE; - f_alloc( uiSize, &pvReturnPtr, pszFile, iLine); - - if( pvReturnPtr) - { - *((FLMUINT *)pvReturnPtr) = FLM_NEW_MEMORY_SIGNATURE; - pvReturnPtr = (void *)(((FLMBYTE *)pvReturnPtr) + FLM_ALIGN_SIZE); - } - - return( pvReturnPtr); -} -#endif - -/**************************************************************************** -Desc: -****************************************************************************/ -#ifdef FLM_DEBUG -void * F_Base::operator new[]( - FLMSIZET uiSize, - const char * pszFile, - int iLine) -#ifndef FLM_NLM - throw() -#endif -{ - void * pvReturnPtr = NULL; - - uiSize += FLM_ALIGN_SIZE; - f_alloc( uiSize, &pvReturnPtr, pszFile, iLine); - - if( pvReturnPtr) - { - *((FLMUINT *)pvReturnPtr) = FLM_NEW_MEMORY_SIGNATURE; - pvReturnPtr = (void *)(((FLMBYTE *)pvReturnPtr) + FLM_ALIGN_SIZE); - } - - return( pvReturnPtr); -} -#endif - -/**************************************************************************** -Desc: -****************************************************************************/ -void F_Base::operator delete( - void * ptr) -{ - if( !ptr) - { - return; - } - - ptr = (void *)(((FLMBYTE *)ptr) - FLM_ALIGN_SIZE); - if( *((FLMUINT *)ptr) != FLM_NEW_MEMORY_SIGNATURE) - { - // Something is wrong with the allocation ... don't - // try to free it. - - flmAssert( 0); - return; - } - - f_free( &ptr); -} - -/**************************************************************************** -Desc: -****************************************************************************/ -void F_Base::operator delete[]( - void * ptr) -{ - if( !ptr) - { - return; - } - - ptr = (void *)(((FLMBYTE *)ptr) - FLM_ALIGN_SIZE); - if( *((FLMUINT *)ptr) != FLM_NEW_MEMORY_SIGNATURE) - { - // Something is wrong with the allocation ... don't - // try to free it. - - flmAssert( 0); - return; - } - - f_free( &ptr); -} - -/**************************************************************************** -Desc: -****************************************************************************/ -#if defined( FLM_DEBUG) && !defined( __WATCOMC__) -void F_Base::operator delete( - void * ptr, - const char *, // file - int) // line -{ - if( !ptr) - { - return; - } - - ptr = (void *)(((FLMBYTE *)ptr) - FLM_ALIGN_SIZE); - if( *((FLMUINT *)ptr) != FLM_NEW_MEMORY_SIGNATURE) - { - // Something is wrong with the allocation ... don't - // try to free it. - - flmAssert( 0); - return; - } - - f_free( &ptr); -} -#endif - -/**************************************************************************** -Desc: -****************************************************************************/ -#if defined( FLM_DEBUG) && !defined( __WATCOMC__) -void F_Base::operator delete[]( - void * ptr, - const char *, // file - int // line - ) -{ - if( !ptr) - { - return; - } - - ptr = (void *)(((FLMBYTE *)ptr) - FLM_ALIGN_SIZE); - if( *((FLMUINT *)ptr) != FLM_NEW_MEMORY_SIGNATURE) - { - // Something is wrong with the allocation ... don't - // try to free it. - - flmAssert( 0); - return; - } - - f_free( &ptr); -} -#endif - -/**************************************************************************** -Desc: -****************************************************************************/ -FLMUINT F_Base::Release( void) -{ - FLMUINT uiRefCnt = --m_ui32RefCnt; - - if( ! uiRefCnt) - { - delete this; - } - - return uiRefCnt; -} - -/**************************************************************************** -Desc: -****************************************************************************/ -FLMEXP void FLMAPI FlmFreeMem( - void * pMem) -{ - f_free( &pMem); -} - -/**************************************************************************** -Desc: -****************************************************************************/ -FLMUINT f_msize( - void * pvPtr) -{ - #if defined( FLM_UNIX) - return( pvPtr ? F_GET_MEM_DATA_SIZE( (pvPtr)) : 0); - #elif defined( FLM_NLM) - return( pvPtr ? (unsigned)SizeOfAllocBlock( - (F_GET_ALLOC_PTR( (pvPtr)))) : 0); - #else - return( pvPtr ? _msize( (F_GET_ALLOC_PTR( (pvPtr)))) : 0); - #endif -} +//------------------------------------------------------------------------- +// Desc: Memory allocation routines. +// Tabs: 3 +// +// Copyright (c) 1991,1993,1995-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: flalloc.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +#undef f_free +#undef f_alloc +#undef f_calloc +#undef f_realloc +#undef f_recalloc + +#ifdef FLM_NLM + extern "C" + { + extern LONG gv_lAllocRTag; + } +#endif + +#ifdef FLM_UNIX + #ifdef HAVE_CONFIG_H + #include "config.h" + #endif + + #ifdef HAVE_DLADDR + #include + #endif + #include + +#endif + +#define F_GET_ALLOC_PTR( pDataPtr) \ + (FLMBYTE *)((FLMBYTE *)(pDataPtr) - sizeof( F_MEM_HDR)) + +#define F_GET_DATA_PTR( pAllocPtr) \ + (FLMBYTE *)((FLMBYTE *)(pAllocPtr) + sizeof( F_MEM_HDR)) + +#define F_GET_MEM_DATA_SIZE( pDataPtr) \ + (((F_MEM_HDR *)(F_GET_ALLOC_PTR( pDataPtr)))->uiDataSize) + +// Picket fence + +#define F_PICKET_FENCE "FFFFFFFF" + +#if defined( FLM_DEBUG) + #define F_PICKET_FENCE_SIZE 8 +#else + #define F_PICKET_FENCE_SIZE 0 +#endif + +#define MEM_PTR_INIT_ARRAY_SIZE 512 +#define MEM_MAX_STACK_WALK_DEPTH 32 + +// If stack tracking is on, leak checking also +// needs to be on. + +#ifndef FLM_DEBUG + #ifdef DEBUG_SIM_OUT_OF_MEM + #undef DEBUG_SIM_OUT_OF_MEM + #endif +#endif + +#ifdef FLM_DEBUG + +// Local function prototypes + +FSTATIC FLMBOOL initMemTracking( + void); + +FSTATIC void saveMemTrackingInfo( + F_MEM_HDR * pHdr); + +FSTATIC void updateMemTrackingInfo( + F_MEM_HDR * pHdr); + +FSTATIC void freeMemTrackingInfo( + FLMBOOL bMutexAlreadyLocked, + FLMUINT uiId, + FLMUINT * puiStack); + +#ifdef DEBUG_SIM_OUT_OF_MEM + +//one of every OUT_OF_MEM_FREQUENCY calls will fail + +#define OUT_OF_MEM_FREQUENCY 40000 + +//OUT_OF_MEM_SEQUENCE_LENGTH calls in a row will fail + +#define OUT_OF_MEM_SEQUENCE_LENGTH 10 + +FLMBOOL SimulateOutOfMemory() +{ + if ( + //is the flag turned on + (gv_FlmSysData.uiOutOfMemSimEnabledFlag == + (FLMUINT)OUT_OF_MEM_SIM_ENABLED_FLAG) && + + //continuing a sequence of failures + ((gv_FlmSysData.uiSimOutOfMemFailSequence > 0) || + + //failing randomly for the first time, and starting a new sequence + (f_randomChoice( &gv_FlmSysData.memSimRandomGen, 0, OUT_OF_MEM_FREQUENCY) == 0))) + { + gv_FlmSysData.uiSimOutOfMemFailTotal++; + gv_FlmSysData.uiSimOutOfMemFailSequence++; + //if reached the end of failure sequence, reset back to 0 so the + //sequence will cease + if ( gv_FlmSysData.uiSimOutOfMemFailSequence >= OUT_OF_MEM_SEQUENCE_LENGTH) + { + gv_FlmSysData.uiSimOutOfMemFailSequence = 0; + } + return TRUE; + } + else + { + return FALSE; + } +} + +#endif //#ifdef DEBUG_SIM_OUT_OF_MEM + +#if defined( FLM_NLM) + + void * memGetEBP(void); + +#ifdef __MWERKS__ + + void * memGetEBP(void) + { + __asm + { + mov eax,[ebp] + } + } + +#else + + #pragma aux memGetEBP = "mov eax,ebp"; + +#endif + +void * memValueAtStackOffset( + void * pos, + int offset); + +#ifdef __MWERKS__ + + void * memValueAtStackOffset( void *, int) + { + __asm + { + mov eax,[ebp+0x8] + mov ebx,[ebp+0xC] + mov eax,ss:[eax+ebx] + } + } + +#else + + #pragma aux memValueAtStackOffset = "mov eax,ss:[eax+ebx]" parm [eax] [ebx]; + +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMUINT * memWalkStack( void) +{ + FLMUINT uiLoop; + FLMUINT uiRtnAddr; + FLMUINT uiEbp = (FLMUINT) memGetEBP(); + FLMUINT uiAddresses [MEM_MAX_STACK_WALK_DEPTH + 1]; + FLMUINT * puiAddresses; + + uiEbp = (FLMUINT) memValueAtStackOffset( (void *)uiEbp, 0); + uiRtnAddr = (FLMUINT) memValueAtStackOffset( (void *)uiEbp, 4); + + for( uiLoop = 0; uiLoop < MEM_MAX_STACK_WALK_DEPTH; uiLoop++) + { + FLMUINT uiOldEbp; + + uiAddresses [uiLoop] = uiRtnAddr; + + if( !uiEbp) + { + break; + } + + uiOldEbp = uiEbp; + uiEbp = (FLMUINT) memValueAtStackOffset( (void *)uiEbp, 0); + + if (!uiEbp || uiEbp <= uiOldEbp || uiEbp > uiOldEbp + 5000) + { + break; + } + + uiRtnAddr = (FLMUINT) memValueAtStackOffset( (void *) uiEbp, 4); + } + + uiAddresses[ uiLoop] = 0; + + if( (puiAddresses = (FLMUINT *)os_malloc( + sizeof( FLMUINT) * (uiLoop+1))) != NULL) + { + f_memcpy( puiAddresses, &uiAddresses [0], + sizeof( FLMUINT) * (uiLoop + 1)); + } + + return( puiAddresses); +} + +#elif defined( FLM_WIN) + +/******************************************************************** +Desc: Reads NSIZE bytes of memory from LPBASEADDRESS +*********************************************************************/ +static BOOL CALLBACK ReadProcMemory( + HANDLE, + DWORD64 lpBaseAddress, + PVOID lpBuffer, + DWORD nSize, + LPDWORD lpNumberOfBytesRead) +{ + static HANDLE hRealProcess = GetCurrentProcess(); + SIZE_T bytesRead = 0; + BOOL rv = ReadProcessMemory(hRealProcess, + (const void *)((FLMUINT)lpBaseAddress & 0xFFFFFFFF), + lpBuffer, SIZE_T(nSize), &bytesRead); + + if (lpNumberOfBytesRead) + { + *lpNumberOfBytesRead = DWORD(bytesRead & 0xFFFFFFFF); + } + + return( rv); +} + +/******************************************************************** +Desc: Walk the call stack. +*********************************************************************/ +FLMUINT * memWalkStack() +{ + STACKFRAME64 stackFrame; + DWORD machineType; + FLMUINT uiLoop; + FLMUINT uiAddresses [MEM_MAX_STACK_WALK_DEPTH + 1]; + FLMUINT * puiAddresses; + HANDLE hProcess = GetCurrentProcess(); + FLMUINT uiAddrCount; + const void * pc; + const void * fp; + + __asm + { + call $ + 5 + pop eax + mov [pc], eax + mov [fp], ebp + } + +#ifdef FLM_64BIT + machineType = IMAGE_FILE_MACHINE_IA64; +#else + machineType = IMAGE_FILE_MACHINE_I386; +#endif + + f_memset( &stackFrame, 0, sizeof( stackFrame)); + + stackFrame.AddrPC.Offset = DWORD((FLMUINT)pc); + stackFrame.AddrPC.Mode = AddrModeFlat; + stackFrame.AddrFrame.Offset = DWORD((FLMUINT)fp); + stackFrame.AddrFrame.Mode = AddrModeFlat; + + f_mutexLock( gv_FlmSysData.hMemTrackingMutex); + + // We have already processed the address inside memWalkStack + + uiAddrCount = 1; + uiLoop = 0; + for (;;) + { + if( !StackWalk64( machineType, hProcess, 0, &stackFrame, + 0, ReadProcMemory, SymFunctionTableAccess64, SymGetModuleBase64, 0)) + { + break; + } + + if( !stackFrame.AddrFrame.Offset) + { + break; + } + + // Skip the first two addresses. These represent the following: + // 1) memWalkStack + // 2) saveMemTrackingInfo or updateMemTrackingInfo + // We don't need to see them in the stack trace. + + uiAddrCount++; + if (uiAddrCount > 2) + { + uiAddresses [uiLoop] = (FLMUINT)stackFrame.AddrReturn.Offset; + uiLoop++; + if (uiLoop == MEM_MAX_STACK_WALK_DEPTH) + { + break; + } + } + } + + f_mutexUnlock( gv_FlmSysData.hMemTrackingMutex); + + uiAddresses [uiLoop] = 0; + if ((puiAddresses = (FLMUINT *)os_malloc( + sizeof( FLMUINT) * (uiLoop+1))) != NULL) + { + f_memcpy( puiAddresses, &uiAddresses [0], sizeof( FLMUINT) * (uiLoop + 1)); + } + return( puiAddresses); +} +#else +FLMUINT * memWalkStack() +{ + return( NULL); +} +#endif + +/******************************************************************** +Desc: Initialize memory tracking +*********************************************************************/ +FSTATIC FLMBOOL initMemTracking( void) +{ + RCODE rc; + F_MUTEX memMutex; + + if (!gv_FlmSysData.bMemTrackingInitialized && !gv_FlmSysData.uiInitThreadId) + { + gv_FlmSysData.uiInitThreadId = f_threadId(); + rc = f_mutexCreate( &memMutex); + f_sleep( 50); + + // Only set to initialized if we were the last thread + // to set gv_FlmSysData.uiInitThreadId + + if (f_threadId() == gv_FlmSysData.uiInitThreadId) + { + if (RC_OK( rc)) + { + gv_FlmSysData.hMemTrackingMutex = memMutex; + } + else + { + gv_FlmSysData.hMemTrackingMutex = F_MUTEX_NULL; + } +#ifdef FLM_WIN + SymSetOptions( SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS); + gv_FlmSysData.hMemProcess = GetCurrentProcess(); + SymInitialize( gv_FlmSysData.hMemProcess, NULL, TRUE); +#endif + gv_FlmSysData.bMemTrackingInitialized = TRUE; + } + else + { + if (RC_OK( rc)) + { + f_mutexDestroy( &memMutex); + } + } + } + + // Go into a loop until we see initialized flag set to TRUE + // Could be another thread that is doing it. + + while (!gv_FlmSysData.bMemTrackingInitialized) + { + f_sleep( 10); + } + return( (gv_FlmSysData.hMemTrackingMutex != F_MUTEX_NULL) ? TRUE : FALSE); +} + +/******************************************************************** +Desc: Save memory tracking information - called on alloc or realloc. +*********************************************************************/ +FSTATIC void saveMemTrackingInfo( + F_MEM_HDR * pHdr) +{ + FLMUINT uiNewCnt; + FLMUINT uiId; + void ** pNew; + + if (gv_FlmSysData.bTrackLeaks && initMemTracking()) + { + f_mutexLock( gv_FlmSysData.hMemTrackingMutex); + + // See if there is enough room in the array + + if (gv_FlmSysData.uiMemNumPtrs == gv_FlmSysData.uiMemTrackingPtrArraySize) + { + + // If array is not initialized, use initial count. Otherwise + // double the size. + + uiNewCnt = (FLMUINT)((!gv_FlmSysData.uiMemTrackingPtrArraySize) + ? MEM_PTR_INIT_ARRAY_SIZE + : gv_FlmSysData.uiMemTrackingPtrArraySize * 2); + if ((pNew = (void **)os_malloc( sizeof( void *) * uiNewCnt)) != NULL) + { + + // Copy the pointers from the old array, if any, + // into the newly allocated array. + + if (gv_FlmSysData.uiMemTrackingPtrArraySize) + { + f_memcpy( pNew, gv_FlmSysData.ppvMemTrackingPtrs, + sizeof( void *) * gv_FlmSysData.uiMemTrackingPtrArraySize); + os_free( gv_FlmSysData.ppvMemTrackingPtrs); + } + f_memset( &pNew [gv_FlmSysData.uiMemTrackingPtrArraySize], 0, + sizeof( void *) * (uiNewCnt - gv_FlmSysData.uiMemTrackingPtrArraySize)); + gv_FlmSysData.ppvMemTrackingPtrs = pNew; + gv_FlmSysData.uiMemTrackingPtrArraySize = uiNewCnt; + } + } + + // If we are still full, we were not able to reallocate memory, so we + // do nothing. + + if (gv_FlmSysData.uiMemNumPtrs == gv_FlmSysData.uiMemTrackingPtrArraySize) + { + pHdr->uiAllocationId = 0; + } + else + { + // Find an empty slot - there has to be one! + + uiId = gv_FlmSysData.uiMemNextPtrSlotToUse; + while (gv_FlmSysData.ppvMemTrackingPtrs [uiId]) + { + if (++uiId == gv_FlmSysData.uiMemTrackingPtrArraySize) + { + uiId = 0; + } + } + + // Allocation ID in the header is offset by one to avoid + // using a value of zero. + + pHdr->uiAllocationId = uiId + 1; + gv_FlmSysData.ppvMemTrackingPtrs [uiId] = pHdr; + gv_FlmSysData.uiMemNumPtrs++; + if ((gv_FlmSysData.uiMemNextPtrSlotToUse = uiId + 1) == + gv_FlmSysData.uiMemTrackingPtrArraySize) + { + gv_FlmSysData.uiMemNextPtrSlotToUse = 0; + } + } + pHdr->uiAllocCnt = ++gv_FlmSysData.uiAllocCnt; + f_mutexUnlock( gv_FlmSysData.hMemTrackingMutex); + } + else + { + pHdr->uiAllocationId = 0; + pHdr->uiAllocCnt = 0; + } + + // Follow the stack. + + if (gv_FlmSysData.bTrackLeaks && gv_FlmSysData.bStackWalk) + { + pHdr->puiStack = memWalkStack(); + } + else + { + pHdr->puiStack = NULL; + } +} + +/******************************************************************** +Desc: Update memory tracking information - called after realloc +*********************************************************************/ +FSTATIC void updateMemTrackingInfo( + F_MEM_HDR * pHdr) +{ + if (pHdr->puiStack) + { + os_free( pHdr->puiStack); + pHdr->puiStack = NULL; + } + if (gv_FlmSysData.bTrackLeaks && gv_FlmSysData.bStackWalk) + { + pHdr->puiStack = memWalkStack(); + } +} + +/******************************************************************** +Desc: Free memory tracking information - called on free. +*********************************************************************/ +FSTATIC void freeMemTrackingInfo( + FLMBOOL bMutexAlreadyLocked, + FLMUINT uiId, + FLMUINT * puiStack + ) +{ + if (uiId) + { + // NOTE: If uiId is non-zero, it means we had to have + // successfully initialized, so we are guaranteed to + // have a mutex. + + if ( !bMutexAlreadyLocked) + { + f_mutexLock( gv_FlmSysData.hMemTrackingMutex); + } + + // Allocation ID in the header is offset by one so that it + // is never zero - a value of zero means that the allocation + // does not have a slot for tracking it in the array. + + gv_FlmSysData.ppvMemTrackingPtrs [uiId - 1] = NULL; + flmAssert( gv_FlmSysData.uiMemNumPtrs); + gv_FlmSysData.uiMemNumPtrs--; + + if ( !bMutexAlreadyLocked) + { + f_mutexUnlock( gv_FlmSysData.hMemTrackingMutex); + } + } + + // Free the stack information, if any. + + if (puiStack) + { + os_free( puiStack); + } +} + +/******************************************************************** +Desc: Log memory leaks. +*********************************************************************/ +void logMemLeak( + F_MEM_HDR * pHdr) +{ + char szMessageBuffer [1024]; + char * pszTmp = &szMessageBuffer [0]; + F_FileHdl * pFileHdl = NULL; + FLMBOOL bOldTrackLeaks = gv_FlmSysData.bTrackLeaks; + + gv_FlmSysData.bTrackLeaks = FALSE; // This ensures that any future + // allocations (for instance, + // allocating the file handle for the + // memtest.ert file) will not try to + // lock the mem tracking mutex. + + + + // Format message to be logged. + + f_strcpy( pszTmp, "Abort=Debug, Retry=Continue, Ignore=Don't Show\r\n"); + while (*pszTmp) + { + pszTmp++; + } +#if defined( FLM_64BIT) + f_sprintf( pszTmp, "Unfreed Pointer: 0x%016I64x\r\n", (FLMUINT)(&pHdr [1])); +#else + f_sprintf( pszTmp, "Unfreed Pointer: 0x%08x\r\n", + (unsigned)((FLMUINT)(&pHdr [1]))); +#endif + while (*pszTmp) + { + pszTmp++; + } + + if (pHdr->pszFileName) + { + f_sprintf( pszTmp, "Source: %s, Line#: %u\r\n", pHdr->pszFileName, + (unsigned)pHdr->iLineNumber); + while (*pszTmp) + { + pszTmp++; + } + } + + if (pHdr->uiAllocCnt) + { + f_sprintf( pszTmp, "Malloc #: %u\r\n", (unsigned)pHdr->uiAllocCnt); + while (*pszTmp) + { + pszTmp++; + } + } + f_sprintf( pszTmp, "Size: %u bytes\r\n", (unsigned)pHdr->uiDataSize); + while (*pszTmp) + { + pszTmp++; + } + + if (pHdr->puiStack) + { + FLMUINT * puiStack = pHdr->puiStack; + FLMUINT uiLen = pszTmp - szMessageBuffer; + char szFuncName [200]; + char * pszFuncName; +#ifdef FLM_WIN + IMAGEHLP_SYMBOL * pImgHlpSymbol; + + pImgHlpSymbol = (IMAGEHLP_SYMBOL *)os_malloc( + sizeof( IMAGEHLP_SYMBOL) + 100); +#endif + + while (*puiStack) + { + szFuncName [0] = 0; +#if defined( FLM_WIN) + if (pImgHlpSymbol) + { +#ifdef FLM_64BIT + DWORD64 udDisplacement; +#else + DWORD udDisplacement; +#endif + + pImgHlpSymbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL); + pImgHlpSymbol->Address = *puiStack; + pImgHlpSymbol->MaxNameLength = 100; + + if (SymGetSymFromAddr( gv_FlmSysData.hMemProcess, *puiStack, + &udDisplacement, pImgHlpSymbol)) + { + f_sprintf( szFuncName, "\t%s + %X\r\n", + (&pImgHlpSymbol->Name [0]), + udDisplacement); + } + } +#elif defined( FLM_NLM) + { + szFuncName [0] = '\t'; + GetClosestSymbol( (BYTE *)(&szFuncName[1]), (LONG)(*puiStack)); + } +#else + +#ifdef HAVE_DLADDR + { + Dl_info dlip; + + if (dladdr( (void *)(*puiStack), &dlip) != 0 && dlip.dli_sname) + { + const char * pszFileName; + if (dlip.dli_saddr != (void *)(*puiStack)) + { + pszFileName = strrchr(dlip.dli_fname, '/'); + if (!pszFileName) + { + pszFileName = dlip.dli_fname; + } + else + { + pszFileName++; // skip over slash + } + f_sprintf( szFuncName, "\t0x%08x (%s)\r\n", + (unsigned)(*puiStack), pszFileName); + } + else + { + f_sprintf( szFuncName, "\t%s\r\n", dlip.dli_sname); + } + } + } +#endif + +#endif + + // If szFuncName [0] is zero, we didn't find a name, so we + // just output the address in HEX. + + if (!szFuncName [0]) + { + f_sprintf( szFuncName, "\t0x%08X\r\n", (unsigned)*puiStack ); + } + + // Output whatever portion of the name will fit into the + // message buffer. + + pszFuncName = &szFuncName [0]; + while (*pszFuncName && uiLen < sizeof( szMessageBuffer) - 1) + { + *pszTmp++ = *pszFuncName++; + uiLen++; + } + + // Process next address in the stack. + + puiStack++; + } + *pszTmp = 0; +#ifdef FLM_WIN + if (pImgHlpSymbol) + { + os_free( pImgHlpSymbol); + } +#endif + } + +#ifdef FLM_WIN + FLMINT iRet; + + iRet = MessageBox( NULL, (LPCTSTR)szMessageBuffer, "WIN Memory Testing", + MB_ABORTRETRYIGNORE | MB_ICONINFORMATION | MB_TASKMODAL + | MB_SETFOREGROUND | MB_DEFBUTTON2); + if (iRet == IDIGNORE) + { + gv_FlmSysData.bLogLeaks = TRUE; + } + else if (iRet == IDABORT) + { + flmAssert( 0); + } +#else + gv_FlmSysData.bLogLeaks = TRUE; +#endif + + if (gv_FlmSysData.bLogLeaks) + { + F_FileSystemImp FileSystem; + RCODE rc; + FLMUINT uiDummy; +#ifdef FLM_NLM + const char * pszErrPath = "sys:\\memtest.ert"; +#else + const char * pszErrPath = "memtest.ert"; +#endif + + if (RC_BAD( rc = FileSystem.Open( pszErrPath, + F_IO_RDWR | F_IO_SH_DENYNONE, &pFileHdl))) + { + if (rc == FERR_IO_PATH_NOT_FOUND) + { + rc = FileSystem.Create( pszErrPath, + F_IO_RDWR | F_IO_SH_DENYNONE, &pFileHdl); + } + } + else + { + FLMUINT uiOffset; + + // Position to append to file. + + rc = pFileHdl->Seek( 0, F_IO_SEEK_END, &uiOffset); + } + + // If we successfully opened the file, write to it. + + if (RC_OK( rc)) + { + if (RC_OK( pFileHdl->Write( F_IO_CURRENT_POS, + (FLMUINT)(pszTmp - &szMessageBuffer [0]), + szMessageBuffer, &uiDummy))) + { + (void)pFileHdl->Flush(); + } + pFileHdl->Close(); + } + } +//Exit: + + gv_FlmSysData.bTrackLeaks = bOldTrackLeaks; + + if (pFileHdl) + { + pFileHdl->Release(); + } +} +#endif + +/******************************************************************** +Desc: Initialize memory - if not already done. +*********************************************************************/ +void f_memoryInit( void) +{ +#ifdef FLM_DEBUG + (void)initMemTracking(); +#endif +} + +/******************************************************************** +Desc: Clean up memory and check for unfreed memory. +*********************************************************************/ +void f_memoryCleanup( void) +{ + +#ifdef FLM_DEBUG + if (initMemTracking()) + { + FLMUINT uiId; + F_MEM_HDR * pHdr; + + f_mutexLock( gv_FlmSysData.hMemTrackingMutex); + for (uiId = 0; uiId < gv_FlmSysData.uiMemTrackingPtrArraySize; uiId++) + { + if ((pHdr = (F_MEM_HDR *)gv_FlmSysData.ppvMemTrackingPtrs [uiId]) != NULL) + { + logMemLeak( pHdr); + freeMemTrackingInfo( TRUE, uiId + 1, pHdr->puiStack); + } + } + + // Free the memory pointer array. + + os_free( gv_FlmSysData.ppvMemTrackingPtrs); + gv_FlmSysData.ppvMemTrackingPtrs = NULL; + gv_FlmSysData.uiMemTrackingPtrArraySize = 0; + gv_FlmSysData.uiMemNumPtrs = 0; + + f_mutexUnlock( gv_FlmSysData.hMemTrackingMutex); + + // Free up the mutex. + + f_mutexDestroy( &gv_FlmSysData.hMemTrackingMutex); + + // Reset to unitialized state. + + gv_FlmSysData.uiInitThreadId = 0; + gv_FlmSysData.hMemTrackingMutex = F_MUTEX_NULL; + gv_FlmSysData.bMemTrackingInitialized = FALSE; +#ifdef FLM_WIN + SymCleanup( gv_FlmSysData.hMemProcess); +#endif + } +#endif +} + +/******************************************************************** +Desc: Allocate Memory. +*********************************************************************/ +RCODE f_alloc( + FLMUINT uiSize, + void ** ppvPtr, + const char * pszFileName, + int iLineNumber) +{ + RCODE rc = FERR_OK; + F_MEM_HDR * pHdr; + +#ifndef FLM_DEBUG + F_UNREFERENCED_PARM( pszFileName); + F_UNREFERENCED_PARM( iLineNumber); +#endif + +#ifdef DEBUG_SIM_OUT_OF_MEM + if ( SimulateOutOfMemory()) + { + *ppvPtr = NULL; + rc = RC_SET( FERR_MEM); + goto Exit; + } +#endif + + if ((pHdr = (F_MEM_HDR *)os_malloc( uiSize + + sizeof( F_MEM_HDR) + + F_PICKET_FENCE_SIZE)) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + pHdr->uiDataSize = uiSize; + *ppvPtr = (void *)(&pHdr [1]); + +#ifdef FLM_DEBUG + pHdr->iLineNumber = iLineNumber; + pHdr->pszFileName = pszFileName; + saveMemTrackingInfo( pHdr); + + #if F_PICKET_FENCE_SIZE + + f_memcpy( ((FLMBYTE *)(*ppvPtr)) + uiSize, + F_PICKET_FENCE, F_PICKET_FENCE_SIZE); + + #endif +#endif + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Allocate and initialize memory. +*********************************************************************/ +RCODE f_calloc( + FLMUINT uiSize, + void ** ppvPtr, + const char * pszFileName, + int iLineNumber) +{ + RCODE rc = FERR_OK; + F_MEM_HDR * pHdr; + +#ifndef FLM_DEBUG + F_UNREFERENCED_PARM( pszFileName); + F_UNREFERENCED_PARM( iLineNumber); +#endif + +#ifdef DEBUG_SIM_OUT_OF_MEM + if ( SimulateOutOfMemory()) + { + *ppvPtr = NULL; + rc = RC_SET( FERR_MEM); + goto Exit; + } +#endif + + if ((pHdr = (F_MEM_HDR *)os_malloc( uiSize + + sizeof( F_MEM_HDR) + + F_PICKET_FENCE_SIZE)) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + pHdr->uiDataSize = uiSize; + *ppvPtr = (void *)(&pHdr [1]); + f_memset( *ppvPtr, 0, uiSize); +#ifdef FLM_DEBUG + pHdr->iLineNumber = iLineNumber; + pHdr->pszFileName = pszFileName; + saveMemTrackingInfo( pHdr); + + #if F_PICKET_FENCE_SIZE + + f_memcpy( ((FLMBYTE *)(*ppvPtr)) + uiSize, + F_PICKET_FENCE, F_PICKET_FENCE_SIZE); + + #endif + +#endif +Exit: + return( rc); +} + +/******************************************************************** +Desc: Reallocate memory. +*********************************************************************/ +RCODE f_realloc( + FLMUINT uiSize, + void ** ppvPtr, + const char * pszFileName, + int iLineNumber) +{ + RCODE rc = FERR_OK; + F_MEM_HDR * pNewHdr; +#ifdef FLM_DEBUG + F_MEM_HDR * pOldHdr; + FLMUINT uiOldAllocationId; + FLMUINT * puiOldStack; +#endif + +#ifdef DEBUG_SIM_OUT_OF_MEM + if ( SimulateOutOfMemory()) + { + *ppvPtr = NULL; + rc = RC_SET( FERR_MEM); + goto Exit; + } +#endif + + if (!(*ppvPtr)) + { + rc = f_alloc( uiSize, ppvPtr, pszFileName, iLineNumber); + goto Exit; + } + +#ifdef FLM_DEBUG + pOldHdr = (F_MEM_HDR *)F_GET_ALLOC_PTR( *ppvPtr); + + #if F_PICKET_FENCE_SIZE + + // Verify the old picket fence + + if (f_memcmp( ((FLMBYTE *)(*ppvPtr)) + pOldHdr->uiDataSize, + F_PICKET_FENCE, F_PICKET_FENCE_SIZE) != 0) + { + flmAssert( 0); + } + + #endif + + uiOldAllocationId = pOldHdr->uiAllocationId; + puiOldStack = pOldHdr->puiStack; +#endif + + if ((pNewHdr = (F_MEM_HDR *)os_realloc( F_GET_ALLOC_PTR( *ppvPtr), + uiSize + sizeof( F_MEM_HDR) + + F_PICKET_FENCE_SIZE)) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + pNewHdr->uiDataSize = uiSize; + *ppvPtr = (void *)(&pNewHdr [1]); +#ifdef FLM_DEBUG + pNewHdr->iLineNumber = iLineNumber; + pNewHdr->pszFileName = pszFileName; + if (pNewHdr != pOldHdr) + { + freeMemTrackingInfo( FALSE, uiOldAllocationId, puiOldStack); + saveMemTrackingInfo( pNewHdr); + } + else + { + updateMemTrackingInfo( pNewHdr); + } + + #if F_PICKET_FENCE_SIZE + + f_memcpy( ((FLMBYTE *)(*ppvPtr)) + uiSize, + F_PICKET_FENCE, F_PICKET_FENCE_SIZE); + + #endif + +#endif + +Exit: + return( rc); +} + +/******************************************************************** +Desc: Reallocate memory, and initialize the new part. +*********************************************************************/ +RCODE f_recalloc( + FLMUINT uiSize, + void ** ppvPtr, + const char * pszFileName, + int iLineNumber) +{ + RCODE rc = FERR_OK; + F_MEM_HDR * pNewHdr; + FLMUINT uiOldSize; +#ifdef FLM_DEBUG + F_MEM_HDR * pOldHdr; + FLMUINT uiOldAllocationId; + FLMUINT * puiOldStack; +#endif + +#ifdef DEBUG_SIM_OUT_OF_MEM + if ( SimulateOutOfMemory()) + { + *ppvPtr = NULL; + rc = RC_SET( FERR_MEM); + goto Exit; + } +#endif + + if (!(*ppvPtr)) + { + rc = f_calloc( uiSize, ppvPtr, pszFileName, iLineNumber); + goto Exit; + } + +#ifdef FLM_DEBUG + pOldHdr = (F_MEM_HDR *)F_GET_ALLOC_PTR( *ppvPtr); + + #if F_PICKET_FENCE_SIZE + + // Verify the old picket fence + + if (f_memcmp( ((FLMBYTE *)(*ppvPtr)) + pOldHdr->uiDataSize, + F_PICKET_FENCE, F_PICKET_FENCE_SIZE) != 0) + { + flmAssert( 0); + } + + #endif + + uiOldAllocationId = pOldHdr->uiAllocationId; + puiOldStack = pOldHdr->puiStack; + +#endif + + uiOldSize = F_GET_MEM_DATA_SIZE( *ppvPtr); + + if ((pNewHdr = (F_MEM_HDR *)os_realloc( F_GET_ALLOC_PTR( *ppvPtr), + uiSize + sizeof( F_MEM_HDR) + + F_PICKET_FENCE_SIZE)) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + pNewHdr->uiDataSize = uiSize; + *ppvPtr = (void *)(&pNewHdr [1]); + if (uiOldSize < uiSize) + { + f_memset( ((FLMBYTE *)(*ppvPtr)) + uiOldSize, 0, + uiSize - uiOldSize); + } +#ifdef FLM_DEBUG + pNewHdr->iLineNumber = iLineNumber; + pNewHdr->pszFileName = pszFileName; + if (pNewHdr != pOldHdr) + { + freeMemTrackingInfo( FALSE, uiOldAllocationId, puiOldStack); + saveMemTrackingInfo( pNewHdr); + } + else + { + updateMemTrackingInfo( pNewHdr); + } + + #if F_PICKET_FENCE_SIZE + + f_memcpy( ((FLMBYTE *)(*ppvPtr)) + uiSize, + F_PICKET_FENCE, F_PICKET_FENCE_SIZE); + + #endif + +#endif +Exit: + return( rc); +} + +/******************************************************************** +Desc: Free previously allocated memory. +*********************************************************************/ +void f_free( + void ** ppvPtr) +{ + if (*ppvPtr) + { + +#ifdef FLM_DEBUG + F_MEM_HDR * pHdr = (F_MEM_HDR *)F_GET_ALLOC_PTR( *ppvPtr); + + #if F_PICKET_FENCE_SIZE + + // Check the picket fence + + if (f_memcmp( ((FLMBYTE *)(*ppvPtr)) + pHdr->uiDataSize, + F_PICKET_FENCE, F_PICKET_FENCE_SIZE) != 0) + { + flmAssert( 0); + } + + #endif + + freeMemTrackingInfo( FALSE, pHdr->uiAllocationId, pHdr->puiStack); +#endif + + os_free( F_GET_ALLOC_PTR( *ppvPtr)); + *ppvPtr = NULL; + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +#ifdef FLM_NLM +void * nlm_realloc( + void * pMemory, + size_t newSize) +{ + void * pNewMemory; + LONG lSize; + + if( !pMemory) + { + pNewMemory = Alloc( newSize, gv_lAllocRTag); + goto Exit; + } + + lSize = SizeOfAllocBlock( pMemory); + + pNewMemory = os_malloc( newSize); + if( !pNewMemory) + { + goto Exit; + } + + if( lSize > newSize) + { + lSize = newSize; + } + + f_memcpy( pNewMemory, pMemory, lSize); + + if( pMemory) + { + Free( pMemory); + } + +Exit: + + return( pNewMemory); +} +#endif + +#undef new +#undef delete +#define FLM_NEW_MEMORY_SIGNATURE 0xABCDABCD + +/**************************************************************************** +Desc: +****************************************************************************/ +void * F_Base::operator new( + FLMSIZET uiSize) +#ifndef FLM_NLM + throw() +#endif +{ + void * pvReturnPtr = NULL; + + uiSize += FLM_ALIGN_SIZE; + f_alloc( uiSize, &pvReturnPtr, "unknown", 0); + + if( pvReturnPtr) + { + *((FLMUINT *)pvReturnPtr) = FLM_NEW_MEMORY_SIGNATURE; + pvReturnPtr = (void *)(((FLMBYTE *)pvReturnPtr) + FLM_ALIGN_SIZE); + } + + return( pvReturnPtr); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void * F_Base::operator new[]( + FLMSIZET uiSize) +#ifndef FLM_NLM + throw() +#endif +{ + void * pvReturnPtr = NULL; + + uiSize += FLM_ALIGN_SIZE; + f_alloc( uiSize, &pvReturnPtr, "unknown", 0); + + if( pvReturnPtr) + { + *((FLMUINT *)pvReturnPtr) = FLM_NEW_MEMORY_SIGNATURE; + pvReturnPtr = (void *)(((FLMBYTE *)pvReturnPtr) + FLM_ALIGN_SIZE); + } + + return( pvReturnPtr); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +#ifdef FLM_DEBUG +void * F_Base::operator new( + FLMSIZET uiSize, + const char * pszFile, + int iLine) +#ifndef FLM_NLM + throw() +#endif +{ + void * pvReturnPtr = NULL; + + uiSize += FLM_ALIGN_SIZE; + f_alloc( uiSize, &pvReturnPtr, pszFile, iLine); + + if( pvReturnPtr) + { + *((FLMUINT *)pvReturnPtr) = FLM_NEW_MEMORY_SIGNATURE; + pvReturnPtr = (void *)(((FLMBYTE *)pvReturnPtr) + FLM_ALIGN_SIZE); + } + + return( pvReturnPtr); +} +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +#ifdef FLM_DEBUG +void * F_Base::operator new[]( + FLMSIZET uiSize, + const char * pszFile, + int iLine) +#ifndef FLM_NLM + throw() +#endif +{ + void * pvReturnPtr = NULL; + + uiSize += FLM_ALIGN_SIZE; + f_alloc( uiSize, &pvReturnPtr, pszFile, iLine); + + if( pvReturnPtr) + { + *((FLMUINT *)pvReturnPtr) = FLM_NEW_MEMORY_SIGNATURE; + pvReturnPtr = (void *)(((FLMBYTE *)pvReturnPtr) + FLM_ALIGN_SIZE); + } + + return( pvReturnPtr); +} +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_Base::operator delete( + void * ptr) +{ + if( !ptr) + { + return; + } + + ptr = (void *)(((FLMBYTE *)ptr) - FLM_ALIGN_SIZE); + if( *((FLMUINT *)ptr) != FLM_NEW_MEMORY_SIGNATURE) + { + // Something is wrong with the allocation ... don't + // try to free it. + + flmAssert( 0); + return; + } + + f_free( &ptr); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_Base::operator delete[]( + void * ptr) +{ + if( !ptr) + { + return; + } + + ptr = (void *)(((FLMBYTE *)ptr) - FLM_ALIGN_SIZE); + if( *((FLMUINT *)ptr) != FLM_NEW_MEMORY_SIGNATURE) + { + // Something is wrong with the allocation ... don't + // try to free it. + + flmAssert( 0); + return; + } + + f_free( &ptr); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +#if defined( FLM_DEBUG) && !defined( __WATCOMC__) +void F_Base::operator delete( + void * ptr, + const char *, // file + int) // line +{ + if( !ptr) + { + return; + } + + ptr = (void *)(((FLMBYTE *)ptr) - FLM_ALIGN_SIZE); + if( *((FLMUINT *)ptr) != FLM_NEW_MEMORY_SIGNATURE) + { + // Something is wrong with the allocation ... don't + // try to free it. + + flmAssert( 0); + return; + } + + f_free( &ptr); +} +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +#if defined( FLM_DEBUG) && !defined( __WATCOMC__) +void F_Base::operator delete[]( + void * ptr, + const char *, // file + int // line + ) +{ + if( !ptr) + { + return; + } + + ptr = (void *)(((FLMBYTE *)ptr) - FLM_ALIGN_SIZE); + if( *((FLMUINT *)ptr) != FLM_NEW_MEMORY_SIGNATURE) + { + // Something is wrong with the allocation ... don't + // try to free it. + + flmAssert( 0); + return; + } + + f_free( &ptr); +} +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMINT F_Base::Release( void) +{ + FLMINT iRefCnt = --m_i32RefCnt; + + if( !iRefCnt) + { + delete this; + } + + return( iRefCnt); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMEXP void FLMAPI FlmFreeMem( + void * pMem) +{ + f_free( &pMem); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMUINT f_msize( + void * pvPtr) +{ + #if defined( FLM_UNIX) + return( pvPtr ? F_GET_MEM_DATA_SIZE( (pvPtr)) : 0); + #elif defined( FLM_NLM) + return( pvPtr ? (unsigned)SizeOfAllocBlock( + (F_GET_ALLOC_PTR( (pvPtr)))) : 0); + #else + return( pvPtr ? _msize( (F_GET_ALLOC_PTR( (pvPtr)))) : 0); + #endif +} diff --git a/flaim/src/flmimon.h b/flaim/src/flmimon.h index 4135dac..baf1561 100644 --- a/flaim/src/flmimon.h +++ b/flaim/src/flmimon.h @@ -731,7 +731,7 @@ public: void Release( F_WebPage ** ppPage); - FINLINE FLMUINT Release( void) + FINLINE FLMINT Release( void) { flmAssert( 0); return( 0); diff --git a/flaim/src/fmutxref.h b/flaim/src/fmutxref.h index 33d3476..24c00f9 100644 --- a/flaim/src/fmutxref.h +++ b/flaim/src/fmutxref.h @@ -79,10 +79,6 @@ public: } }; - // AddRef/Release provided by F_Base - //FLMUINT AddRef(); // Increment ref count for this object - //FLMUINT Release(); // Decrement ref count, if 0 then delete. - private: FLMUINT m_uiLockCount; // Number of times the semaphore has diff --git a/flaim/src/fposix.cpp b/flaim/src/fposix.cpp index cea908f..a784974 100644 --- a/flaim/src/fposix.cpp +++ b/flaim/src/fposix.cpp @@ -31,6 +31,7 @@ #define _LARGE_FILES #endif #include + #include #endif #include @@ -40,12 +41,10 @@ #define O_SYNC 0 #endif -#if !defined( O_DSYNC) // some systems don't have this - #define O_DSYNC O_SYNC +#if !defined( O_DSYNC) + #define O_DSYNC O_SYNC #endif -extern RCODE gv_CriticalFSError; - #define MAX_CREATION_TRIES 10 #if defined( FLM_SOLARIS) @@ -54,6 +53,20 @@ extern RCODE gv_CriticalFSError; #include #endif +extern RCODE gv_CriticalFSError; + +#if FLM_USE_SPIN_LOCK_ATOMICS + + #if defined( FLM_SPARC) + volatile unsigned char gv_flmAtomicLock; + #endif + +#elif FLM_USE_MUTEX_ATOMICS + + FSTATIC pthread_mutex_t gv_flmAtomicLock = PTHREAD_MUTEX_INITIALIZER; + +#endif + /**************************************************************************** Desc: ****************************************************************************/ @@ -1523,110 +1536,14 @@ FLMUINT flmGetFSBlockSize( return( uiFSBlkSize); } -/**************************************************************************** -Desc: -****************************************************************************/ -#if defined(__GNUC__) && defined(__i386__) -__attribute__((always_inline)) -static FINLINE unsigned int atomic_inc( - volatile unsigned int * p) -{ - unsigned int rv; - - __asm__ __volatile__( - "movl $1, %%eax\n\t" - "lock\n\t" - "xaddl %%eax, (%%ecx)\n\t" - "incl %%eax" - : "=a" (rv) - : "c" (p) - ); - - return rv; -} -#endif +#endif // FLM_UNIX /**************************************************************************** Desc: ****************************************************************************/ -#if defined(__GNUC__) && defined(__i386__) -FLMUINT32 ftkAtomicIncrement( - FLMUINT32 * pui32Target) +#if defined( FLM_WATCOM_NLM) +int fposixDummy(void) { - return( atomic_inc( pui32Target)); + return( 0); } #endif - -/**************************************************************************** -Desc: -****************************************************************************/ -#if defined(__GNUC__) && defined(__i386__) -__attribute__((always_inline)) -static FINLINE unsigned int atomic_dec( - volatile unsigned int * p) -{ - unsigned int rv; - - __asm__ __volatile__( - "movl $-1, %%eax\n\t" - "lock\n\t" - "xaddl %%eax, (%%ecx)\n\t" - "decl %%eax" - : "=a" (rv) - : "c" (p) - ); // result left in eax - - return rv; -} -#endif - -/**************************************************************************** -Desc: -****************************************************************************/ -#if defined(__GNUC__) && defined(__i386__) -FLMUINT32 ftkAtomicDecrement( - FLMUINT32 * pui32Target) -{ - return( atomic_dec( pui32Target)); -} -#endif - -/**************************************************************************** -Desc: -****************************************************************************/ -#if defined(__GNUC__) && defined(__i386__) -__attribute__((always_inline)) -static FINLINE unsigned int atomic_xchg( - volatile unsigned int * p, - unsigned int i) -{ - unsigned int rv; - - __asm__ __volatile__( - "xchgl %%eax, (%%ecx)" - : "=a" (rv) - : "c" (p), "a" (i) - ); // result left in eax, no buslock required for xchgl - - return rv; -} -#endif - -/**************************************************************************** -Desc: -****************************************************************************/ -#if defined(__GNUC__) && defined(__i386__) -FLMUINT32 ftkAtomicExchange( - FLMUINT32 * puiTarget, - FLMUINT32 ui32Value) -{ - return( atomic_xchg( puiTarget, ui32Value)); -} -#endif - -#elif defined( FLM_WATCOM_NLM) - int fposixDummy(void) - { - return( 0); - } -#endif diff --git a/flaim/src/frec.cpp b/flaim/src/frec.cpp index 1d47097..402061e 100644 --- a/flaim/src/frec.cpp +++ b/flaim/src/frec.cpp @@ -1692,12 +1692,12 @@ RCODE FlmRecord::compactMemory( void) FLMBOOL bHeapAlloc = FALSE; flmAssert( isCached()); - flmAssert( m_ui32RefCnt == 1); + flmAssert( m_i32RefCnt == 1); // Temporarily increment the reference count so that we don't hit // debug asserts while processing - m_ui32RefCnt++; + m_i32RefCnt++; if( !m_uiBufferSize || (!m_bHolesInData && m_uiDataBufOffset == getDataBufSize())) @@ -1952,7 +1952,7 @@ Exit: // Remove the reference count added above - m_ui32RefCnt--; + m_i32RefCnt--; return( rc); } @@ -2710,46 +2710,26 @@ Exit: /***************************************************************************** Desc: Add a globally shared reference to this object. *****************************************************************************/ -FLMUINT FlmRecord::AddRef( - FLMBOOL bMutexLocked) +FLMINT FlmRecord::AddRef( void) { - FLMUINT uiRefCnt = 0; - FLMBOOL bLockedMutex = FALSE; + FLMINT iRefCnt = 0; -#ifdef ATOMIC_INCDEC_SUPPORT - (void)bMutexLocked; - uiRefCnt = (FLMUINT)ftkAtomicIncrement( &m_ui32RefCnt); -#else + iRefCnt = ftkAtomicIncrement( &m_i32RefCnt); - if( !bMutexLocked) - { - f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); - bLockedMutex = TRUE; - bMutexLocked = TRUE; - } - - uiRefCnt = ++m_ui32RefCnt; -#endif - - if( bLockedMutex) - { - f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); - } - flmAssert( uiRefCnt > 1); - return( uiRefCnt); + flmAssert( iRefCnt > 1); + return( iRefCnt); } /***************************************************************************** Desc: Removes a globally shared reference to this object. *****************************************************************************/ -FLMUINT FlmRecord::Release( +FLMINT FlmRecord::Release( FLMBOOL bMutexLocked) { - FLMUINT uiRefCnt = 0; + FLMINT iRefCnt = 0; FLMBOOL bUnlockMutex = FALSE; -#ifdef ATOMIC_INCDEC_SUPPORT - if( isCached() && m_ui32RefCnt == 2) + if( isCached() && m_i32RefCnt == 2) { if( !bMutexLocked) { @@ -2759,26 +2739,16 @@ FLMUINT FlmRecord::Release( } } - uiRefCnt = (FLMUINT)ftkAtomicDecrement( &m_ui32RefCnt); -#else - if( !bMutexLocked) - { - f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); - bMutexLocked = TRUE; - bUnlockMutex = TRUE; - } + iRefCnt = ftkAtomicDecrement( &m_i32RefCnt); - uiRefCnt = --m_ui32RefCnt; -#endif - - if( !uiRefCnt) + if( !iRefCnt) { flmAssert( !isCached()); m_uiFlags |= RCA_OK_TO_DELETE; delete this; } - else if( bMutexLocked && uiRefCnt == 1 && isCached()) + else if( bMutexLocked && iRefCnt == 1 && isCached()) { // If the record is still cached, and the reference count // is 1, it is safe to compact the record's buffer @@ -2809,7 +2779,7 @@ FLMUINT FlmRecord::Release( f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); } - return( uiRefCnt); + return( iRefCnt); } /***************************************************************************** @@ -4585,7 +4555,7 @@ void FlmRecord::operator delete( return; } - flmAssert( ((FlmRecord *)ptr)->m_ui32RefCnt == 0); + flmAssert( ((FlmRecord *)ptr)->m_i32RefCnt == 0); gv_FlmSysData.RCacheMgr.pRecAlloc->freeCell( ptr); } @@ -4614,7 +4584,7 @@ void FlmRecord::operator delete( return; } - flmAssert( ((FlmRecord *)ptr)->m_ui32RefCnt == 0); + flmAssert( ((FlmRecord *)ptr)->m_i32RefCnt == 0); gv_FlmSysData.RCacheMgr.pRecAlloc->freeCell( ptr); } #endif diff --git a/flaim/src/fsrvlock.cpp b/flaim/src/fsrvlock.cpp index f22152e..031dca6 100644 --- a/flaim/src/fsrvlock.cpp +++ b/flaim/src/fsrvlock.cpp @@ -562,12 +562,15 @@ ServerLockObject::ServerLockObject() m_bStartTimeSet = FALSE; } -FLMUINT ServerLockObject::Release( +/**************************************************************************** +Desc: +****************************************************************************/ +FLMINT ServerLockObject::Release( F_MutexRef * pMutexRef) { - FLMUINT uiRefCnt = --m_ui32RefCnt; + FLMINT iRefCnt = --m_i32RefCnt; - if( !uiRefCnt) + if( !iRefCnt) { delete this; goto Exit; @@ -576,7 +579,7 @@ FLMUINT ServerLockObject::Release( // When it is no longer pointed to from anything but the server lock // manager, put it into the avail list. - if (uiRefCnt == 1) + if (iRefCnt == 1) { LOCK_WAITER * pLockWaiter; F_MutexRef TmpMutexRef( m_pServerLockMgr->GetSemPtr()); @@ -612,7 +615,7 @@ FLMUINT ServerLockObject::Release( Exit: - return uiRefCnt; + return( iRefCnt); } /**************************************************************************** @@ -1045,8 +1048,10 @@ RCODE ServerLockObject::Unlock( // At this point we should still have our reference to the object, // the lock manager's reference, and a reference from at least one // waiter (that may have been granted above). - flmAssert( m_ui32RefCnt >= 3); - m_ui32RefCnt--; + + flmAssert( m_i32RefCnt >= 3); + + m_i32RefCnt--; } MutexRef.Unlock(); diff --git a/flaim/src/fsrvlock.h b/flaim/src/fsrvlock.h index 43eaa21..82ac102 100644 --- a/flaim/src/fsrvlock.h +++ b/flaim/src/fsrvlock.h @@ -332,10 +332,10 @@ public: FLMBOOL bRelease = FALSE, DB_STATS * pDbStats = NULL); - FLMUINT Release( + FLMINT Release( F_MutexRef * pMutexRef); - FINLINE FLMUINT Release( void) + FINLINE FLMINT Release( void) { return( Release( NULL)); } diff --git a/flaim/src/fstructs.h b/flaim/src/fstructs.h index a96e723..c46f4f8 100644 --- a/flaim/src/fstructs.h +++ b/flaim/src/fstructs.h @@ -2308,9 +2308,9 @@ public: void * getKey( FLMUINT * puiKeyLen = NULL); - FLMUINT AddRef(); + FLMINT AddRef(); - FLMUINT Release(); + FLMINT Release(); FINLINE eHashObjType objectType( void) { diff --git a/flaim/src/fsuperfl.cpp b/flaim/src/fsuperfl.cpp index b0502fd..f32f456 100644 --- a/flaim/src/fsuperfl.cpp +++ b/flaim/src/fsuperfl.cpp @@ -1,1325 +1,1325 @@ -//------------------------------------------------------------------------- -// Desc: Super-file class implementation. -// Tabs: 3 -// -// Copyright (c) 1998-2003,2005-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: fsuperfl.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ -//------------------------------------------------------------------------- - -#include "flaimsys.h" - -FSTATIC FLMBYTE base24ToDigit( - FLMUINT uiBaseValue); - -/**************************************************************************** -Public: F_FileIdList -Desc: Constructor -****************************************************************************/ -F_FileIdList::F_FileIdList() -{ - m_hMutex = F_MUTEX_NULL; - m_uiFileIdTblSize = 0; - m_puiFileIdTbl = NULL; -} - -/**************************************************************************** -Public: ~F_FileIdList -Desc: Destructor -****************************************************************************/ -F_FileIdList::~F_FileIdList() -{ - if( m_hMutex != F_MUTEX_NULL) - { - f_mutexDestroy( &m_hMutex); - } - - if( m_puiFileIdTbl) - { - for( FLMUINT uiLoop = 0; uiLoop < m_uiFileIdTblSize; uiLoop++) - { - if( m_puiFileIdTbl[ uiLoop]) - { - (void)gv_FlmSysData.pFileHdlMgr->Remove( - m_puiFileIdTbl[ uiLoop]); - } - } - - f_free( &m_puiFileIdTbl); - } -} - -/**************************************************************************** -Public: setup -Desc: Allocates the mutex used by the file ID list object -****************************************************************************/ -RCODE F_FileIdList::setup( void) -{ - RCODE rc = FERR_OK; - - flmAssert( m_hMutex == F_MUTEX_NULL); - - if( RC_BAD( rc = f_mutexCreate( &m_hMutex))) - { - goto Exit; - } - -Exit: - - return( rc); -} - -/**************************************************************************** -Public: getFileId -Desc: Translates a database file number into a file ID. -****************************************************************************/ -RCODE F_FileIdList::getFileId( - FLMUINT uiFileNumber, - FLMUINT * puiFileId) -{ - RCODE rc = FERR_OK; - FLMBOOL bMutexLocked = TRUE; - FLMUINT uiLoop = 0; - - f_mutexLock( m_hMutex); - bMutexLocked = TRUE; - - if( uiFileNumber >= m_uiFileIdTblSize) - { - FLMUINT uiOldTableSize = m_uiFileIdTblSize; - - /* - Re-size the table - */ - - if( RC_BAD( rc = f_recalloc( (uiFileNumber + 1) * sizeof( FLMUINT), - &m_puiFileIdTbl))) - { - goto Exit; - } - m_uiFileIdTblSize = uiFileNumber + 1; - - for( uiLoop = uiOldTableSize; uiLoop < m_uiFileIdTblSize; uiLoop++) - { - m_puiFileIdTbl[ uiLoop] = gv_FlmSysData.pFileHdlMgr->GetUniqueId(); - } - } - - *puiFileId = m_puiFileIdTbl[ uiFileNumber]; - -Exit: - - if( bMutexLocked) - { - f_mutexUnlock( m_hMutex); - } - - return( rc); -} - -/**************************************************************************** -Public: F_SuperFileHdl -Desc: Constructor -****************************************************************************/ -F_SuperFileHdl::F_SuperFileHdl( void) -{ - m_pFileIdList = NULL; - m_pszDbFileName = NULL; - m_pszDataFileNameBase = NULL; - f_memset( &m_CheckedOutFileHdls[ 0], 0, sizeof( m_CheckedOutFileHdls)); - m_pCheckedOutFileHdls = &m_CheckedOutFileHdls [0]; - m_uiCkoArraySize = MAX_CHECKED_OUT_FILE_HDLS + 1; - m_uiBlockSize = 0; - m_uiExtendSize = DEFAULT_FILE_EXTEND_SIZE; - m_uiMaxAutoExtendSize = gv_FlmSysData.uiMaxFileSize; - m_uiDbVersion = 0; - m_uiLowestDirtySlot = 1; - m_uiHighestDirtySlot = 0; - m_uiHighestUsedSlot = 0; - m_uiHighestFileNumber = 0; - m_pECacheMgr = NULL; - m_bMinimizeFlushes = FALSE; - m_bSetupCalled = FALSE; -} - -/**************************************************************************** -Public: F_SuperFileHdl -Desc: Destructor -****************************************************************************/ -F_SuperFileHdl::~F_SuperFileHdl() -{ - /* - Release any file handles still being held and close the files. - */ - - if( m_bSetupCalled) - { - (void)ReleaseFiles( TRUE); - } - - /* - Release the ID list - */ - - if( m_pFileIdList) - { - m_pFileIdList->Release(); - } - - if (m_pszDbFileName) - { - f_free( &m_pszDbFileName); - } -} - -/**************************************************************************** -Public: Setup -Desc: Configures the super file object -****************************************************************************/ -RCODE F_SuperFileHdl::Setup( - F_FileIdList * pFileIdList, - const char * pszDbFileName, - const char * pszDataDir) -{ - RCODE rc = FERR_OK; - FLMUINT uiNameLen; - FLMUINT uiDataNameLen; - char szDir[ F_PATH_MAX_SIZE]; - char szBaseName[ F_FILENAME_SIZE]; - - flmAssert( !m_bSetupCalled); - - if( !pszDbFileName && *pszDbFileName == 0) - { - rc = RC_SET( FERR_IO_INVALID_PATH); - goto Exit; - } - - if( !pFileIdList) - { - if( (m_pFileIdList = f_new F_FileIdList) == NULL) - { - rc = RC_SET( FERR_MEM); - goto Exit; - } - - if( RC_BAD( rc = m_pFileIdList->setup())) - { - FLMUINT uiRefCnt; - - uiRefCnt = m_pFileIdList->Release(); - flmAssert( !uiRefCnt); - m_pFileIdList = NULL; - goto Exit; - } - } - else - { - pFileIdList->AddRef(); - m_pFileIdList = pFileIdList; - } - - uiNameLen = f_strlen( pszDbFileName); - if (pszDataDir && *pszDataDir) - { - if (RC_BAD( rc = f_pathReduce( pszDbFileName, szDir, szBaseName))) - { - goto Exit; - } - f_strcpy( szDir, pszDataDir); - if (RC_BAD( rc = f_pathAppend( szDir, szBaseName))) - { - goto Exit; - } - uiDataNameLen = f_strlen( szDir); - - if (RC_BAD( rc = f_alloc( (uiNameLen + 1) + (uiDataNameLen + 1), - &m_pszDbFileName))) - { - goto Exit; - } - - f_memcpy( m_pszDbFileName, pszDbFileName, uiNameLen + 1); - m_pszDataFileNameBase = m_pszDbFileName + uiNameLen + 1; - flmGetDbBasePath( m_pszDataFileNameBase, szDir, &m_uiDataExtOffset); - m_uiExtOffset = uiNameLen - (uiDataNameLen - m_uiDataExtOffset); - } - else - { - if (RC_BAD( rc = f_alloc( (uiNameLen + 1) * 2, &m_pszDbFileName))) - { - goto Exit; - } - - f_memcpy( m_pszDbFileName, pszDbFileName, uiNameLen + 1); - m_pszDataFileNameBase = m_pszDbFileName + uiNameLen + 1; - flmGetDbBasePath( m_pszDataFileNameBase, - m_pszDbFileName, &m_uiDataExtOffset); - m_uiExtOffset = m_uiDataExtOffset; - } - - m_bSetupCalled = TRUE; - -Exit: - - return( rc); -} - -/**************************************************************************** -Public: CreateFile -Desc: Creates a file -****************************************************************************/ -RCODE F_SuperFileHdl::CreateFile( - FLMUINT uiFileNumber) -{ - RCODE rc = FERR_OK; - char szFilePath[ F_PATH_MAX_SIZE]; - F_FileHdlImp * pFileHdl = NULL; - FLMUINT uiFileId; - - flmAssert( m_bSetupCalled && m_uiDbVersion && m_uiBlockSize); - flmAssert( uiFileNumber <= MAX_LOG_BLOCK_FILE_NUMBER ( m_uiDbVersion)); - - // See if we already have an open file handle (or if we can open the file). - // If so, truncate the file and use it. - - if( RC_OK( rc = GetFileHdl( uiFileNumber, TRUE, &pFileHdl))) - { - rc = pFileHdl->Truncate( 0); - pFileHdl = NULL; - goto Exit; - } - else if( rc != FERR_IO_PATH_NOT_FOUND) - { - goto Exit; - } - - // The file was not found above. Allocate a new file handle. - - if( (pFileHdl = f_new F_FileHdlImp) == NULL) - { - rc = RC_SET( FERR_MEM); - goto Exit; - } - -#ifdef FLM_WIN - pFileHdl->SetBlockSize( m_uiBlockSize); -#endif - - // Configure the file handle. - - if( RC_BAD( rc = m_pFileIdList->getFileId( uiFileNumber, &uiFileId))) - { - goto Exit; - } - - flmAssert( uiFileId); // File ID should always be non-zero - - if( RC_BAD( rc = pFileHdl->Setup( uiFileId))) - { - goto Exit; - } - - // Build the file path - - if( RC_BAD( rc = GetFilePath( uiFileNumber, szFilePath))) - { - goto Exit; - } - - if( RC_BAD( rc = pFileHdl->Create( szFilePath, - F_IO_RDWR | F_IO_EXCL | F_IO_SH_DENYNONE | F_IO_DIRECT))) - { - goto Exit; - } - - // Insert into the file handle manager - - if( RC_BAD( rc = gv_FlmSysData.pFileHdlMgr->InsertNew( pFileHdl))) - { - goto Exit; - } - -Exit: - - if( pFileHdl) - { - pFileHdl->Release(); - } - - return( rc); -} - -/**************************************************************************** -Public: ReadBlock -Desc: Reads a database block into a buffer -****************************************************************************/ -RCODE F_SuperFileHdl::ReadBlock( - FLMUINT uiBlkAddress, - FLMUINT uiBytesToRead, - void * pvBuffer, - FLMUINT * puiBytesRead) -{ - RCODE rc = FERR_OK; - F_FileHdlImp * pFileHdl = NULL; - - flmAssert( m_bSetupCalled && m_uiDbVersion && m_uiBlockSize); - - if( m_pECacheMgr) - { - flmAssert( uiBytesToRead <= m_uiBlockSize); - - if( RC_OK( rc = m_pECacheMgr->getBlock( uiBlkAddress, - (FLMBYTE *)pvBuffer, uiBytesToRead))) - { - if( puiBytesRead) - { - *puiBytesRead = uiBytesToRead; - } - goto Exit; - } - else if( rc != FERR_NOT_FOUND) - { - goto Exit; - } - else - { - // Drop through and read the block from disk. - rc = FERR_OK; - } - } - - if( RC_BAD( rc = GetFileHdl( - FSGetFileNumber( uiBlkAddress), FALSE, &pFileHdl))) - { - goto Exit; - } - - if( RC_BAD( rc = pFileHdl->SectorRead( - FSGetFileOffset( uiBlkAddress), uiBytesToRead, - pvBuffer, puiBytesRead))) - { - if (rc != FERR_IO_END_OF_FILE && rc != FERR_MEM) - { - ReleaseFile( FSGetFileNumber( uiBlkAddress), TRUE); - } - goto Exit; - } - - if( m_pECacheMgr) - { - m_pECacheMgr->putBlock( uiBlkAddress, (FLMBYTE *)pvBuffer); - } - -Exit: - - return( rc); -} - -/**************************************************************************** -Public: WriteBlock -Desc: Writes a block to the database -****************************************************************************/ -RCODE F_SuperFileHdl::WriteBlock( - FLMUINT uiBlkAddress, - FLMUINT uiBytesToWrite, - void * pvBuffer, - FLMUINT uiBufferSize, - F_IOBuffer * pIOBuffer, - FLMUINT * puiBytesWritten) -{ - RCODE rc = FERR_OK; - FLMUINT uiLoop; - F_FileHdlImp * pFileHdl = NULL; - FLMBYTE * pucBlk; - - flmAssert( m_bSetupCalled && m_uiDbVersion && m_uiBlockSize); - - if( RC_BAD( rc = GetFileHdl( - FSGetFileNumber( uiBlkAddress), TRUE, &pFileHdl))) - { - goto Exit; - } - - pFileHdl->setExtendSize( m_uiExtendSize); - pFileHdl->setMaxAutoExtendSize( m_uiMaxAutoExtendSize); - if( RC_BAD( rc = pFileHdl->SectorWrite( - FSGetFileOffset( uiBlkAddress), uiBytesToWrite, - pvBuffer, uiBufferSize, pIOBuffer, puiBytesWritten))) - { - if (rc != FERR_IO_DISK_FULL && rc != FERR_MEM) - { - ReleaseFile( FSGetFileNumber( uiBlkAddress), TRUE); - } - goto Exit; - } - -Exit: - - if( m_pECacheMgr) - { - if( RC_OK( rc) && !pIOBuffer) - { - for( uiLoop = 0; uiLoop < uiBytesToWrite; uiLoop += m_uiBlockSize) - { - pucBlk = &(((FLMBYTE *)pvBuffer)[ uiLoop]); - m_pECacheMgr->putBlock( uiBlkAddress + uiLoop, pucBlk); - } - } - else - { - for( uiLoop = 0; uiLoop < uiBytesToWrite; uiLoop += m_uiBlockSize) - { - (void)m_pECacheMgr->invalidateBlock( uiBlkAddress + uiLoop); - } - } - } - - return( rc); -} - -/**************************************************************************** -Public: ReadHeader -Desc: Reads data from the database header -****************************************************************************/ -RCODE F_SuperFileHdl::ReadHeader( - FLMUINT uiOffset, - FLMUINT uiBytesToRead, - void * pvBuffer, - FLMUINT * puiBytesRead) -{ - RCODE rc = FERR_OK; - F_FileHdlImp * pFileHdl; - -#ifdef FLM_DEBUG - if( m_uiBlockSize) - { - /* - Note: Block size may not be set because we are in the process of - opening the file for the first time and we don't know the block - size until after the header has been read. - */ - - flmAssert( (FLMUINT)(uiOffset + uiBytesToRead) <= m_uiBlockSize); - } -#endif - - if( RC_BAD( rc = GetFileHdl( 0, TRUE, &pFileHdl))) - { - goto Exit; - } - - if( RC_BAD( rc = pFileHdl->Read( uiOffset, - uiBytesToRead, pvBuffer, puiBytesRead))) - { - if (rc != FERR_IO_END_OF_FILE && rc != FERR_MEM) - { - ReleaseFile( (FLMUINT)0, TRUE); - } - goto Exit; - } - -Exit: - - return( rc); -} - -/**************************************************************************** -Public: WriteHeader -Desc: Writes data to the database header -****************************************************************************/ -RCODE F_SuperFileHdl::WriteHeader( - FLMUINT uiOffset, - FLMUINT uiBytesToWrite, - void * pvBuffer, - FLMUINT * puiBytesWritten) -{ - RCODE rc = FERR_OK; - F_FileHdlImp * pFileHdl; - -#ifdef FLM_DEBUG - if( m_uiBlockSize) - { - flmAssert( (FLMUINT)(uiOffset + uiBytesToWrite) <= m_uiBlockSize); - } -#endif - - if( RC_BAD( rc = GetFileHdl( 0, TRUE, &pFileHdl))) - { - goto Exit; - } - - if( RC_BAD( rc = pFileHdl->Write( uiOffset, - uiBytesToWrite, pvBuffer, puiBytesWritten))) - { - if (rc != FERR_IO_DISK_FULL && rc != FERR_MEM) - { - ReleaseFile( (FLMUINT)0, TRUE); - } - goto Exit; - } - -Exit: - - return( rc); -} - -/**************************************************************************** -Public: ReleaseFile -Desc: Releases all file handle objects and optionally closes the files -****************************************************************************/ -RCODE F_SuperFileHdl::ReleaseFile( - FLMUINT uiFileNum, - FLMBOOL bCloseFile) -{ - RCODE rc = FERR_OK; - CHECKED_OUT_FILE_HDL * pCkoFileHdl; - FLMUINT uiSlot; - - pCkoFileHdl = getCkoFileHdlPtr( uiFileNum, &uiSlot); - if( pCkoFileHdl->uiFileNumber == uiFileNum) - { - if( RC_BAD( rc = ReleaseFile( pCkoFileHdl, bCloseFile))) - { - goto Exit; - } - } - -Exit: - - return( rc); -} - -/**************************************************************************** -Public: ReleaseFiles -Desc: Releases all file handle objects and optionally closes the files -****************************************************************************/ -RCODE F_SuperFileHdl::ReleaseFiles( - FLMBOOL bCloseFiles) -{ - RCODE rc = FERR_OK; - FLMUINT uiLoop; - - flmAssert( m_bSetupCalled); - - for( uiLoop = 0; uiLoop <= m_uiHighestUsedSlot; uiLoop++) - { - if( RC_BAD( rc = ReleaseFile( - &m_CheckedOutFileHdls[ uiLoop], bCloseFiles))) - { - goto Exit; - } - } - -Exit: - - return( rc); -} - -/**************************************************************************** -Public: ReleaseFile -Desc: Releases a file handle object -****************************************************************************/ -RCODE F_SuperFileHdl::ReleaseFile( - CHECKED_OUT_FILE_HDL * pCkoFileHdl, - FLMBOOL bCloseFile) -{ - RCODE rc = FERR_OK; - F_FileHdlImp * pFileHdl = pCkoFileHdl->pFileHdl; - - if( pFileHdl) - { - flmAssert( pFileHdl->GetFileId()); - - if( pCkoFileHdl->bDirty) - { - (void)pFileHdl->Flush(); - } - - if( bCloseFile) - { - FLMUINT uiRefCnt; - - /* - We must remove this handle from all lists and release - the file handle. - */ - - rc = gv_FlmSysData.pFileHdlMgr->Remove( pFileHdl); - uiRefCnt = pFileHdl->Release(); - flmAssert( uiRefCnt == 0); // pFileHdl should have been freed. - } - else - { - /* - Link out of the used list and move to the available list. - */ - - rc = gv_FlmSysData.pFileHdlMgr->MakeAvailAndRelease( pFileHdl); - - /* - NOTE: MakeAvailAndRelease will perform a release on the - file handle object for the caller. - */ - } - - clearCkoFileHdl( pCkoFileHdl); - } - - return( rc); -} - -/**************************************************************************** -Desc: Copy one CKO array into another. -****************************************************************************/ -void F_SuperFileHdl::copyCkoFileHdls( - CHECKED_OUT_FILE_HDL * pSrcCkoArray, - FLMUINT uiSrcHighestUsedSlot - ) -{ - FLMUINT uiNewSlot; - FLMUINT uiSrcSlot; - - // Zeroeth element is always copied. - - f_memcpy( m_pCheckedOutFileHdls, pSrcCkoArray, - sizeof( CHECKED_OUT_FILE_HDL)); - - // Memset the rest of the destination array to zero. - - f_memset( &m_pCheckedOutFileHdls[1], 0, sizeof( CHECKED_OUT_FILE_HDL) * - (m_uiCkoArraySize - 1)); - - m_uiHighestUsedSlot = 0; - m_uiLowestDirtySlot = 1; - m_uiHighestDirtySlot = 0; - for (uiSrcSlot = 1, pSrcCkoArray++; - uiSrcSlot <= uiSrcHighestUsedSlot; - uiSrcSlot++, pSrcCkoArray++) - { - if (pSrcCkoArray->pFileHdl && pSrcCkoArray->uiFileNumber) - { - uiNewSlot = pSrcCkoArray->uiFileNumber % (m_uiCkoArraySize - 1) + 1; - - // Only overwrite the destination one if the file number is - // lower than the one already there - - if (pSrcCkoArray->uiFileNumber < - m_pCheckedOutFileHdls [uiNewSlot].uiFileNumber || - !m_pCheckedOutFileHdls [uiNewSlot].uiFileNumber) - { - if (m_pCheckedOutFileHdls [uiNewSlot].uiFileNumber) - { - ReleaseFile( &m_pCheckedOutFileHdls [uiNewSlot], FALSE); - } - f_memcpy( &m_pCheckedOutFileHdls [uiNewSlot], pSrcCkoArray, - sizeof( CHECKED_OUT_FILE_HDL)); - if (uiNewSlot > m_uiHighestUsedSlot) - { - m_uiHighestUsedSlot = uiNewSlot; - } - if (m_uiHighestFileNumber < pSrcCkoArray->uiFileNumber) - { - m_uiHighestFileNumber = pSrcCkoArray->uiFileNumber; - } - if (pSrcCkoArray->bDirty) - { - if (m_uiLowestDirtySlot > m_uiHighestDirtySlot) - - { - m_uiLowestDirtySlot = - m_uiHighestDirtySlot = uiNewSlot; - } - else if( m_uiHighestDirtySlot < uiNewSlot) - { - m_uiHighestDirtySlot = uiNewSlot; - } - else if (m_uiLowestDirtySlot < uiNewSlot) - { - m_uiLowestDirtySlot = uiNewSlot; - } - } - } - else - { - ReleaseFile( pSrcCkoArray, FALSE); - } - } - } -} - -/**************************************************************************** -Desc: Disable flush minimizing. -****************************************************************************/ -void F_SuperFileHdl::disableFlushMinimize( void) -{ - - // Copy the allocated array back into the fixed array. - // This doesn't necessarily copy all of the file handles. - - if (m_pCheckedOutFileHdls != &m_CheckedOutFileHdls [0]) - { - CHECKED_OUT_FILE_HDL * pOldCkoArray = m_pCheckedOutFileHdls; - FLMUINT uiOldHighestUsedSlot = m_uiHighestUsedSlot; - - m_pCheckedOutFileHdls = &m_CheckedOutFileHdls [0]; - m_uiCkoArraySize = MAX_CHECKED_OUT_FILE_HDLS + 1; - copyCkoFileHdls( pOldCkoArray, uiOldHighestUsedSlot); - - f_free( &pOldCkoArray); - } - m_bMinimizeFlushes = FALSE; -} - -/**************************************************************************** -Desc: Flush dirty files to disk. -****************************************************************************/ -RCODE F_SuperFileHdl::Flush( void) -{ - RCODE rc = FERR_OK; - FLMUINT uiLoop; - - // Flush all dirty files - - for (uiLoop = m_uiLowestDirtySlot; - uiLoop <= m_uiHighestDirtySlot; - uiLoop++) - { - if( m_pCheckedOutFileHdls[ uiLoop].bDirty) - { - RCODE tmpRc; - - if (RC_BAD( tmpRc = - m_pCheckedOutFileHdls[ uiLoop].pFileHdl->Flush())) - { - rc = tmpRc; - ReleaseFile( &m_pCheckedOutFileHdls [uiLoop], TRUE); - } - m_pCheckedOutFileHdls[ uiLoop].bDirty = FALSE; - } - } - m_uiLowestDirtySlot = 1; - m_uiHighestDirtySlot = 0; - return( rc); -} - -/**************************************************************************** -Public: TruncateFile -Desc: Truncates back to an end of file block address. - This may only be called from reduce() because there cannot - be any other cases to reduce a 3x block file. -****************************************************************************/ -RCODE F_SuperFileHdl::TruncateFile( - FLMUINT uiEOFBlkAddress) -{ - RCODE rc = FERR_OK; - FLMUINT uiFileNumber = (FLMUINT)FSGetFileNumber( uiEOFBlkAddress); - FLMUINT uiBlockOffset = (FLMUINT)FSGetFileOffset( uiEOFBlkAddress); - F_FileHdlImp * pFileHdl; - - /* - Truncate the current block file. - */ - - if( RC_BAD( rc = GetFileHdl( uiFileNumber, TRUE, &pFileHdl))) - { - goto Exit; - } - - if( RC_BAD( rc = pFileHdl->Truncate( uiBlockOffset))) - { - ReleaseFile( uiFileNumber, TRUE); - goto Exit; - } - - /* - Visit the rest of the high block files until a NULL file hdl is hit. - */ - - for( ;;) - { - if( RC_BAD( GetFileHdl( ++uiFileNumber, TRUE, &pFileHdl))) - { - break; - } - - if( RC_BAD( rc = pFileHdl->Truncate( (FLMUINT)0 ))) - { - ReleaseFile( uiFileNumber, TRUE); - goto Exit; - } - - if( RC_BAD( rc = ReleaseFile( uiFileNumber, TRUE))) - { - goto Exit; - } - } - -Exit: - - return( rc); -} - -/**************************************************************************** -Public: TruncateFiles -Desc: Truncate to zero length any files between the specified start - and end files. -****************************************************************************/ -void F_SuperFileHdl::TruncateFiles( - FLMUINT uiStartFileNum, - FLMUINT uiEndFileNum) -{ - FLMUINT uiFileNumber; - F_FileHdlImp * pFileHdl; - - for( uiFileNumber = uiStartFileNum; - uiFileNumber <= uiEndFileNum; - uiFileNumber++ ) - { - if( RC_OK( GetFileHdl( uiFileNumber, TRUE, &pFileHdl ))) - { - (void)pFileHdl->Truncate( (FLMUINT)0 ); - (void)ReleaseFile( uiFileNumber, TRUE); - } - } -} - -/**************************************************************************** -Public: GetFileSize -Desc: Returns the physical size of a file -****************************************************************************/ -RCODE F_SuperFileHdl::GetFileSize( - FLMUINT uiFileNumber, - FLMUINT * puiFileSize) -{ - F_FileHdlImp * pFileHdl = NULL; - RCODE rc = FERR_OK; - - flmAssert( m_bSetupCalled); - flmAssert( puiFileSize); - - /* - Initialize the size to zero. - */ - - *puiFileSize = 0; - - /* - Get the file handle. - */ - - if( RC_BAD( rc = GetFileHdl( uiFileNumber, FALSE, &pFileHdl))) - { - goto Exit; - } - - if( RC_BAD( rc = pFileHdl->Size( puiFileSize))) - { - ReleaseFile( uiFileNumber, TRUE); - goto Exit; - } - -Exit: - - return( rc); -} - -/**************************************************************************** -Public: GetFilePath -Desc: Returns the path of a file given its file number -****************************************************************************/ -RCODE F_SuperFileHdl::GetFilePath( - FLMUINT uiFileNumber, - char * pszIoPath) -{ - RCODE rc = FERR_OK; - FLMUINT uiExtOffset; - - // Sanity checks - - flmAssert( m_bSetupCalled); - - if (!uiFileNumber) - { - f_strcpy( pszIoPath, m_pszDbFileName); - goto Exit; - } - - if ((m_uiDbVersion >= FLM_VER_4_3 && - uiFileNumber <= MAX_DATA_FILE_NUM_VER43) || - uiFileNumber <= MAX_DATA_FILE_NUM_VER40) - { - f_memcpy( pszIoPath, m_pszDataFileNameBase, m_uiDataExtOffset); - uiExtOffset = m_uiDataExtOffset; - } - else - { - f_memcpy( pszIoPath, m_pszDbFileName, m_uiExtOffset); - uiExtOffset = m_uiExtOffset; - } - - // Modify the file's extension. - - bldSuperFileExtension( m_uiDbVersion, - uiFileNumber, &pszIoPath[ uiExtOffset]); - -Exit: - - return( rc); -} - -/**************************************************************************** -Desc: Reallocates the checked out file handle array. -****************************************************************************/ -RCODE F_SuperFileHdl::reallocCkoArray( - FLMUINT uiFileNum - ) -{ - RCODE rc = FERR_OK; - FLMUINT uiNewSize; - CHECKED_OUT_FILE_HDL * pNewCkoArray; - CHECKED_OUT_FILE_HDL * pOldCkoArray; - FLMUINT uiOldHighestUsedSlot; - - if (uiFileNum < m_uiHighestFileNumber) - { - uiFileNum = m_uiHighestFileNumber; - } - uiNewSize = uiFileNum + 128; - - // Reallocate so we can guarantee that all of the current file - // numbers will copy and there is room for this new one as well. - - if (uiNewSize > MAX_LOG_BLOCK_FILE_NUMBER( m_uiDbVersion) + 1) - { - flmAssert( uiFileNum <= MAX_LOG_BLOCK_FILE_NUMBER( m_uiDbVersion)); - uiNewSize = MAX_LOG_BLOCK_FILE_NUMBER( m_uiDbVersion) + 1; - } - - // No need to call f_calloc, because copyCkoFileHdls will initialize - // it below. - - if (RC_BAD( rc = f_alloc( sizeof( CHECKED_OUT_FILE_HDL) * uiNewSize, - &pNewCkoArray))) - { - goto Exit; - } - - pOldCkoArray = m_pCheckedOutFileHdls; - uiOldHighestUsedSlot = m_uiHighestUsedSlot; - - m_pCheckedOutFileHdls = pNewCkoArray; - m_uiCkoArraySize = uiNewSize; - - copyCkoFileHdls( pOldCkoArray, uiOldHighestUsedSlot); - - // Can't free the old one until after the copy! - - if (pOldCkoArray != &m_CheckedOutFileHdls [0]) - { - f_free( &pOldCkoArray); - } - -Exit: - - return( rc); - -} - -/**************************************************************************** -Public: GetFileHdl -Desc: Returns a file handle given the file's number -****************************************************************************/ -RCODE F_SuperFileHdl::GetFileHdl( - FLMUINT uiFileNum, - FLMBOOL bGetForUpdate, - F_FileHdlImp ** ppFileHdl) -{ - RCODE rc = FERR_OK; - F_FileHdlImp * pFileHdl = NULL; - FLMUINT uiFileId; - CHECKED_OUT_FILE_HDL * pCkoFileHdl; - char szFilePath[ F_PATH_MAX_SIZE]; - FLMUINT uiSlot; - - // Get the file handle - - pCkoFileHdl = getCkoFileHdlPtr( uiFileNum, &uiSlot); - if( pCkoFileHdl->uiFileNumber != uiFileNum && - pCkoFileHdl->pFileHdl) - { - if (pCkoFileHdl->bDirty && m_bMinimizeFlushes) - { - flmAssert( pCkoFileHdl->uiFileNumber); - if (RC_BAD( reallocCkoArray( uiFileNum))) - { - goto Exit; - } - pCkoFileHdl = getCkoFileHdlPtr( uiFileNum, &uiSlot); - - // Better have reallocated so that the new slot for - // the file number has nothing in it. - - flmAssert( !pCkoFileHdl->uiFileNumber && - !pCkoFileHdl->pFileHdl); - } - else - { - if( RC_BAD( rc = ReleaseFile( pCkoFileHdl, FALSE))) - { - goto Exit; - } - } - } - - if( !pCkoFileHdl->pFileHdl) - { - // Get the file ID - - if( RC_BAD( rc = m_pFileIdList->getFileId( uiFileNum, &uiFileId))) - { - goto Exit; - } - - // Look for an available file handle if not opening exclusive. - // NOTE: AddRef() performed for caller by FindAvail if a file - // handle is found. - - if( RC_BAD( gv_FlmSysData.pFileHdlMgr->FindAvail( - uiFileId, FALSE, &pFileHdl))) - { - // Allocate a new file handle, open the file and - // link into the used directory. - - if( (pFileHdl = f_new F_FileHdlImp) == NULL) - { - rc = RC_SET( FERR_MEM); - goto Exit; - } - -#ifdef FLM_WIN - /* - If m_uiBlockSize is 0, direct I/O will not be used - */ - - pFileHdl->SetBlockSize( m_uiBlockSize); -#endif - - flmAssert( uiFileId); // File ID must be non-zero - - if( RC_BAD( rc = pFileHdl->Setup( uiFileId))) - { - goto Exit; - } - - // Build the file path - - if( RC_BAD( rc = GetFilePath( uiFileNum, szFilePath))) - { - goto Exit; - } - - // Open the file - - if( RC_BAD( rc = pFileHdl->Open( szFilePath, - F_IO_RDWR | F_IO_SH_DENYNONE | F_IO_DIRECT))) - { - goto Exit; - } - - // Insert into the manager - - if( RC_BAD( rc = gv_FlmSysData.pFileHdlMgr->InsertNew( pFileHdl))) - { - goto Exit; - } - } - - pCkoFileHdl->pFileHdl = pFileHdl; - pFileHdl = NULL; // Set to NULL so the handle won't be released below - pCkoFileHdl->uiFileNumber = uiFileNum; - pCkoFileHdl->bDirty = FALSE; - if( m_uiHighestUsedSlot < uiSlot) - { - m_uiHighestUsedSlot = uiSlot; - } - if (m_uiHighestFileNumber < uiFileNum) - { - m_uiHighestFileNumber = uiFileNum; - } - } - - *ppFileHdl = pCkoFileHdl->pFileHdl; - if( bGetForUpdate) - { - pCkoFileHdl->bDirty = TRUE; - if (m_uiLowestDirtySlot > m_uiHighestDirtySlot) - - { - m_uiLowestDirtySlot = - m_uiHighestDirtySlot = uiSlot; - } - else if( m_uiHighestDirtySlot < uiSlot) - { - m_uiHighestDirtySlot = uiSlot; - } - else if (m_uiLowestDirtySlot < uiSlot) - { - m_uiLowestDirtySlot = uiSlot; - } - } - -Exit: - - if( pFileHdl) - { - pFileHdl->Release(); - } - - return( rc); -} - -/**************************************************************************** -Desc: Generates a file name given a super file number. - Adds ".xx" to pFileExtension. Use lower case characters. -Notes: This is a base 24 alphanumeric value where - { a, b, c, d, e, f, i, l, o, r, u, v } values are removed. -****************************************************************************/ -void bldSuperFileExtension( - FLMUINT uiDbVersion, - FLMUINT uiFileNum, - char * pszFileExtension) -{ - FLMBYTE ucLetter; - - flmAssert( uiDbVersion); // Make sure the database version is valid - - if (uiDbVersion >= FLM_VER_4_3) - { - if (uiFileNum <= MAX_DATA_FILE_NUM_VER43 - 1536) - { - // No additional letter - File numbers 1 to 511 - // This is just like pre-4.3 numbering. - ucLetter = 0; - } - else if (uiFileNum <= MAX_DATA_FILE_NUM_VER43 - 1024) - { - // File numbers 512 to 1023 - ucLetter = 'r'; - } - else if (uiFileNum <= MAX_DATA_FILE_NUM_VER43 - 512) - { - // File numbers 1024 to 1535 - ucLetter = 's'; - } - else if (uiFileNum <= MAX_DATA_FILE_NUM_VER43) - { - // File numbers 1536 to 2047 - ucLetter = 't'; - } - else if (uiFileNum <= MAX_LOG_FILE_NUM_VER43 - 1536) - { - // File numbers 2048 to 2559 - ucLetter = 'v'; - } - else if (uiFileNum <= MAX_LOG_FILE_NUM_VER43 - 1024) - { - // File numbers 2560 to 3071 - ucLetter = 'w'; - } - else if (uiFileNum <= MAX_LOG_FILE_NUM_VER43 - 512) - { - // File numbers 3072 to 3583 - ucLetter = 'x'; - } - else - { - flmAssert( uiFileNum <= MAX_LOG_FILE_NUM_VER43); - - // File numbers 3584 to 4095 - ucLetter = 'z'; - } - } - else // Pre-4.3 versions - { - if (uiFileNum <= MAX_DATA_FILE_NUM_VER40) - { - // No additional letter - File numbers 1 to 511 - // This is just like pre-4.3 numbering. - ucLetter = 0; - } - else - { - flmAssert( uiFileNum <= MAX_LOG_FILE_NUM_VER40); - - // File numbers 512 to 1023 - ucLetter = 'x'; - } - } - - *pszFileExtension++ = '.'; - *pszFileExtension++ = base24ToDigit( (uiFileNum & 511) / 24); - *pszFileExtension++ = base24ToDigit( (uiFileNum & 511) % 24); - *pszFileExtension++ = ucLetter; - *pszFileExtension = 0; -} - -/**************************************************************************** -Desc: Turn a base 24 value into a native alphanumeric value. -Notes: This is a base 24 alphanumeric value where - {a, b, c, d, e, f, i, l, o, r, u, v } values are removed. -****************************************************************************/ -FSTATIC FLMBYTE base24ToDigit( - FLMUINT uiValue) -{ - flmAssert( uiValue <= 23); - - if( uiValue <= 9) - { - uiValue += (FLMUINT) NATIVE_ZERO; - } - else - { - uiValue = f_toascii(uiValue) - 10 + (FLMUINT)f_toascii('g'); - if( uiValue >= (FLMUINT)'i') - { - uiValue++; - if( uiValue >= (FLMUINT)'l') - { - uiValue++; - if( uiValue >= (FLMUINT)'o') - { - uiValue++; - if( uiValue >= (FLMUINT)'r') - { - uiValue++; - if( uiValue >= (FLMUINT)'u') - { - uiValue++; - if( uiValue >= (FLMUINT)'v') - { - uiValue++; - } - } - } - } - } - } - } - return (FLMBYTE)uiValue; -} +//------------------------------------------------------------------------- +// Desc: Super-file class implementation. +// Tabs: 3 +// +// Copyright (c) 1998-2003,2005-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: fsuperfl.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +FSTATIC FLMBYTE base24ToDigit( + FLMUINT uiBaseValue); + +/**************************************************************************** +Public: F_FileIdList +Desc: Constructor +****************************************************************************/ +F_FileIdList::F_FileIdList() +{ + m_hMutex = F_MUTEX_NULL; + m_uiFileIdTblSize = 0; + m_puiFileIdTbl = NULL; +} + +/**************************************************************************** +Public: ~F_FileIdList +Desc: Destructor +****************************************************************************/ +F_FileIdList::~F_FileIdList() +{ + if( m_hMutex != F_MUTEX_NULL) + { + f_mutexDestroy( &m_hMutex); + } + + if( m_puiFileIdTbl) + { + for( FLMUINT uiLoop = 0; uiLoop < m_uiFileIdTblSize; uiLoop++) + { + if( m_puiFileIdTbl[ uiLoop]) + { + (void)gv_FlmSysData.pFileHdlMgr->Remove( + m_puiFileIdTbl[ uiLoop]); + } + } + + f_free( &m_puiFileIdTbl); + } +} + +/**************************************************************************** +Public: setup +Desc: Allocates the mutex used by the file ID list object +****************************************************************************/ +RCODE F_FileIdList::setup( void) +{ + RCODE rc = FERR_OK; + + flmAssert( m_hMutex == F_MUTEX_NULL); + + if( RC_BAD( rc = f_mutexCreate( &m_hMutex))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Public: getFileId +Desc: Translates a database file number into a file ID. +****************************************************************************/ +RCODE F_FileIdList::getFileId( + FLMUINT uiFileNumber, + FLMUINT * puiFileId) +{ + RCODE rc = FERR_OK; + FLMBOOL bMutexLocked = TRUE; + FLMUINT uiLoop = 0; + + f_mutexLock( m_hMutex); + bMutexLocked = TRUE; + + if( uiFileNumber >= m_uiFileIdTblSize) + { + FLMUINT uiOldTableSize = m_uiFileIdTblSize; + + /* + Re-size the table + */ + + if( RC_BAD( rc = f_recalloc( (uiFileNumber + 1) * sizeof( FLMUINT), + &m_puiFileIdTbl))) + { + goto Exit; + } + m_uiFileIdTblSize = uiFileNumber + 1; + + for( uiLoop = uiOldTableSize; uiLoop < m_uiFileIdTblSize; uiLoop++) + { + m_puiFileIdTbl[ uiLoop] = gv_FlmSysData.pFileHdlMgr->GetUniqueId(); + } + } + + *puiFileId = m_puiFileIdTbl[ uiFileNumber]; + +Exit: + + if( bMutexLocked) + { + f_mutexUnlock( m_hMutex); + } + + return( rc); +} + +/**************************************************************************** +Public: F_SuperFileHdl +Desc: Constructor +****************************************************************************/ +F_SuperFileHdl::F_SuperFileHdl( void) +{ + m_pFileIdList = NULL; + m_pszDbFileName = NULL; + m_pszDataFileNameBase = NULL; + f_memset( &m_CheckedOutFileHdls[ 0], 0, sizeof( m_CheckedOutFileHdls)); + m_pCheckedOutFileHdls = &m_CheckedOutFileHdls [0]; + m_uiCkoArraySize = MAX_CHECKED_OUT_FILE_HDLS + 1; + m_uiBlockSize = 0; + m_uiExtendSize = DEFAULT_FILE_EXTEND_SIZE; + m_uiMaxAutoExtendSize = gv_FlmSysData.uiMaxFileSize; + m_uiDbVersion = 0; + m_uiLowestDirtySlot = 1; + m_uiHighestDirtySlot = 0; + m_uiHighestUsedSlot = 0; + m_uiHighestFileNumber = 0; + m_pECacheMgr = NULL; + m_bMinimizeFlushes = FALSE; + m_bSetupCalled = FALSE; +} + +/**************************************************************************** +Public: F_SuperFileHdl +Desc: Destructor +****************************************************************************/ +F_SuperFileHdl::~F_SuperFileHdl() +{ + /* + Release any file handles still being held and close the files. + */ + + if( m_bSetupCalled) + { + (void)ReleaseFiles( TRUE); + } + + /* + Release the ID list + */ + + if( m_pFileIdList) + { + m_pFileIdList->Release(); + } + + if (m_pszDbFileName) + { + f_free( &m_pszDbFileName); + } +} + +/**************************************************************************** +Public: Setup +Desc: Configures the super file object +****************************************************************************/ +RCODE F_SuperFileHdl::Setup( + F_FileIdList * pFileIdList, + const char * pszDbFileName, + const char * pszDataDir) +{ + RCODE rc = FERR_OK; + FLMUINT uiNameLen; + FLMUINT uiDataNameLen; + char szDir[ F_PATH_MAX_SIZE]; + char szBaseName[ F_FILENAME_SIZE]; + + flmAssert( !m_bSetupCalled); + + if( !pszDbFileName && *pszDbFileName == 0) + { + rc = RC_SET( FERR_IO_INVALID_PATH); + goto Exit; + } + + if( !pFileIdList) + { + if( (m_pFileIdList = f_new F_FileIdList) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = m_pFileIdList->setup())) + { + FLMINT iRefCnt; + + iRefCnt = m_pFileIdList->Release(); + flmAssert( !iRefCnt); + m_pFileIdList = NULL; + goto Exit; + } + } + else + { + pFileIdList->AddRef(); + m_pFileIdList = pFileIdList; + } + + uiNameLen = f_strlen( pszDbFileName); + if (pszDataDir && *pszDataDir) + { + if (RC_BAD( rc = f_pathReduce( pszDbFileName, szDir, szBaseName))) + { + goto Exit; + } + f_strcpy( szDir, pszDataDir); + if (RC_BAD( rc = f_pathAppend( szDir, szBaseName))) + { + goto Exit; + } + uiDataNameLen = f_strlen( szDir); + + if (RC_BAD( rc = f_alloc( (uiNameLen + 1) + (uiDataNameLen + 1), + &m_pszDbFileName))) + { + goto Exit; + } + + f_memcpy( m_pszDbFileName, pszDbFileName, uiNameLen + 1); + m_pszDataFileNameBase = m_pszDbFileName + uiNameLen + 1; + flmGetDbBasePath( m_pszDataFileNameBase, szDir, &m_uiDataExtOffset); + m_uiExtOffset = uiNameLen - (uiDataNameLen - m_uiDataExtOffset); + } + else + { + if (RC_BAD( rc = f_alloc( (uiNameLen + 1) * 2, &m_pszDbFileName))) + { + goto Exit; + } + + f_memcpy( m_pszDbFileName, pszDbFileName, uiNameLen + 1); + m_pszDataFileNameBase = m_pszDbFileName + uiNameLen + 1; + flmGetDbBasePath( m_pszDataFileNameBase, + m_pszDbFileName, &m_uiDataExtOffset); + m_uiExtOffset = m_uiDataExtOffset; + } + + m_bSetupCalled = TRUE; + +Exit: + + return( rc); +} + +/**************************************************************************** +Public: CreateFile +Desc: Creates a file +****************************************************************************/ +RCODE F_SuperFileHdl::CreateFile( + FLMUINT uiFileNumber) +{ + RCODE rc = FERR_OK; + char szFilePath[ F_PATH_MAX_SIZE]; + F_FileHdlImp * pFileHdl = NULL; + FLMUINT uiFileId; + + flmAssert( m_bSetupCalled && m_uiDbVersion && m_uiBlockSize); + flmAssert( uiFileNumber <= MAX_LOG_BLOCK_FILE_NUMBER ( m_uiDbVersion)); + + // See if we already have an open file handle (or if we can open the file). + // If so, truncate the file and use it. + + if( RC_OK( rc = GetFileHdl( uiFileNumber, TRUE, &pFileHdl))) + { + rc = pFileHdl->Truncate( 0); + pFileHdl = NULL; + goto Exit; + } + else if( rc != FERR_IO_PATH_NOT_FOUND) + { + goto Exit; + } + + // The file was not found above. Allocate a new file handle. + + if( (pFileHdl = f_new F_FileHdlImp) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + +#ifdef FLM_WIN + pFileHdl->SetBlockSize( m_uiBlockSize); +#endif + + // Configure the file handle. + + if( RC_BAD( rc = m_pFileIdList->getFileId( uiFileNumber, &uiFileId))) + { + goto Exit; + } + + flmAssert( uiFileId); // File ID should always be non-zero + + if( RC_BAD( rc = pFileHdl->Setup( uiFileId))) + { + goto Exit; + } + + // Build the file path + + if( RC_BAD( rc = GetFilePath( uiFileNumber, szFilePath))) + { + goto Exit; + } + + if( RC_BAD( rc = pFileHdl->Create( szFilePath, + F_IO_RDWR | F_IO_EXCL | F_IO_SH_DENYNONE | F_IO_DIRECT))) + { + goto Exit; + } + + // Insert into the file handle manager + + if( RC_BAD( rc = gv_FlmSysData.pFileHdlMgr->InsertNew( pFileHdl))) + { + goto Exit; + } + +Exit: + + if( pFileHdl) + { + pFileHdl->Release(); + } + + return( rc); +} + +/**************************************************************************** +Public: ReadBlock +Desc: Reads a database block into a buffer +****************************************************************************/ +RCODE F_SuperFileHdl::ReadBlock( + FLMUINT uiBlkAddress, + FLMUINT uiBytesToRead, + void * pvBuffer, + FLMUINT * puiBytesRead) +{ + RCODE rc = FERR_OK; + F_FileHdlImp * pFileHdl = NULL; + + flmAssert( m_bSetupCalled && m_uiDbVersion && m_uiBlockSize); + + if( m_pECacheMgr) + { + flmAssert( uiBytesToRead <= m_uiBlockSize); + + if( RC_OK( rc = m_pECacheMgr->getBlock( uiBlkAddress, + (FLMBYTE *)pvBuffer, uiBytesToRead))) + { + if( puiBytesRead) + { + *puiBytesRead = uiBytesToRead; + } + goto Exit; + } + else if( rc != FERR_NOT_FOUND) + { + goto Exit; + } + else + { + // Drop through and read the block from disk. + rc = FERR_OK; + } + } + + if( RC_BAD( rc = GetFileHdl( + FSGetFileNumber( uiBlkAddress), FALSE, &pFileHdl))) + { + goto Exit; + } + + if( RC_BAD( rc = pFileHdl->SectorRead( + FSGetFileOffset( uiBlkAddress), uiBytesToRead, + pvBuffer, puiBytesRead))) + { + if (rc != FERR_IO_END_OF_FILE && rc != FERR_MEM) + { + ReleaseFile( FSGetFileNumber( uiBlkAddress), TRUE); + } + goto Exit; + } + + if( m_pECacheMgr) + { + m_pECacheMgr->putBlock( uiBlkAddress, (FLMBYTE *)pvBuffer); + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Public: WriteBlock +Desc: Writes a block to the database +****************************************************************************/ +RCODE F_SuperFileHdl::WriteBlock( + FLMUINT uiBlkAddress, + FLMUINT uiBytesToWrite, + void * pvBuffer, + FLMUINT uiBufferSize, + F_IOBuffer * pIOBuffer, + FLMUINT * puiBytesWritten) +{ + RCODE rc = FERR_OK; + FLMUINT uiLoop; + F_FileHdlImp * pFileHdl = NULL; + FLMBYTE * pucBlk; + + flmAssert( m_bSetupCalled && m_uiDbVersion && m_uiBlockSize); + + if( RC_BAD( rc = GetFileHdl( + FSGetFileNumber( uiBlkAddress), TRUE, &pFileHdl))) + { + goto Exit; + } + + pFileHdl->setExtendSize( m_uiExtendSize); + pFileHdl->setMaxAutoExtendSize( m_uiMaxAutoExtendSize); + if( RC_BAD( rc = pFileHdl->SectorWrite( + FSGetFileOffset( uiBlkAddress), uiBytesToWrite, + pvBuffer, uiBufferSize, pIOBuffer, puiBytesWritten))) + { + if (rc != FERR_IO_DISK_FULL && rc != FERR_MEM) + { + ReleaseFile( FSGetFileNumber( uiBlkAddress), TRUE); + } + goto Exit; + } + +Exit: + + if( m_pECacheMgr) + { + if( RC_OK( rc) && !pIOBuffer) + { + for( uiLoop = 0; uiLoop < uiBytesToWrite; uiLoop += m_uiBlockSize) + { + pucBlk = &(((FLMBYTE *)pvBuffer)[ uiLoop]); + m_pECacheMgr->putBlock( uiBlkAddress + uiLoop, pucBlk); + } + } + else + { + for( uiLoop = 0; uiLoop < uiBytesToWrite; uiLoop += m_uiBlockSize) + { + (void)m_pECacheMgr->invalidateBlock( uiBlkAddress + uiLoop); + } + } + } + + return( rc); +} + +/**************************************************************************** +Public: ReadHeader +Desc: Reads data from the database header +****************************************************************************/ +RCODE F_SuperFileHdl::ReadHeader( + FLMUINT uiOffset, + FLMUINT uiBytesToRead, + void * pvBuffer, + FLMUINT * puiBytesRead) +{ + RCODE rc = FERR_OK; + F_FileHdlImp * pFileHdl; + +#ifdef FLM_DEBUG + if( m_uiBlockSize) + { + /* + Note: Block size may not be set because we are in the process of + opening the file for the first time and we don't know the block + size until after the header has been read. + */ + + flmAssert( (FLMUINT)(uiOffset + uiBytesToRead) <= m_uiBlockSize); + } +#endif + + if( RC_BAD( rc = GetFileHdl( 0, TRUE, &pFileHdl))) + { + goto Exit; + } + + if( RC_BAD( rc = pFileHdl->Read( uiOffset, + uiBytesToRead, pvBuffer, puiBytesRead))) + { + if (rc != FERR_IO_END_OF_FILE && rc != FERR_MEM) + { + ReleaseFile( (FLMUINT)0, TRUE); + } + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Public: WriteHeader +Desc: Writes data to the database header +****************************************************************************/ +RCODE F_SuperFileHdl::WriteHeader( + FLMUINT uiOffset, + FLMUINT uiBytesToWrite, + void * pvBuffer, + FLMUINT * puiBytesWritten) +{ + RCODE rc = FERR_OK; + F_FileHdlImp * pFileHdl; + +#ifdef FLM_DEBUG + if( m_uiBlockSize) + { + flmAssert( (FLMUINT)(uiOffset + uiBytesToWrite) <= m_uiBlockSize); + } +#endif + + if( RC_BAD( rc = GetFileHdl( 0, TRUE, &pFileHdl))) + { + goto Exit; + } + + if( RC_BAD( rc = pFileHdl->Write( uiOffset, + uiBytesToWrite, pvBuffer, puiBytesWritten))) + { + if (rc != FERR_IO_DISK_FULL && rc != FERR_MEM) + { + ReleaseFile( (FLMUINT)0, TRUE); + } + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Public: ReleaseFile +Desc: Releases all file handle objects and optionally closes the files +****************************************************************************/ +RCODE F_SuperFileHdl::ReleaseFile( + FLMUINT uiFileNum, + FLMBOOL bCloseFile) +{ + RCODE rc = FERR_OK; + CHECKED_OUT_FILE_HDL * pCkoFileHdl; + FLMUINT uiSlot; + + pCkoFileHdl = getCkoFileHdlPtr( uiFileNum, &uiSlot); + if( pCkoFileHdl->uiFileNumber == uiFileNum) + { + if( RC_BAD( rc = ReleaseFile( pCkoFileHdl, bCloseFile))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Public: ReleaseFiles +Desc: Releases all file handle objects and optionally closes the files +****************************************************************************/ +RCODE F_SuperFileHdl::ReleaseFiles( + FLMBOOL bCloseFiles) +{ + RCODE rc = FERR_OK; + FLMUINT uiLoop; + + flmAssert( m_bSetupCalled); + + for( uiLoop = 0; uiLoop <= m_uiHighestUsedSlot; uiLoop++) + { + if( RC_BAD( rc = ReleaseFile( + &m_CheckedOutFileHdls[ uiLoop], bCloseFiles))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Public: ReleaseFile +Desc: Releases a file handle object +****************************************************************************/ +RCODE F_SuperFileHdl::ReleaseFile( + CHECKED_OUT_FILE_HDL * pCkoFileHdl, + FLMBOOL bCloseFile) +{ + RCODE rc = FERR_OK; + F_FileHdlImp * pFileHdl = pCkoFileHdl->pFileHdl; + + if( pFileHdl) + { + flmAssert( pFileHdl->GetFileId()); + + if( pCkoFileHdl->bDirty) + { + (void)pFileHdl->Flush(); + } + + if( bCloseFile) + { + FLMINT iRefCnt; + + /* + We must remove this handle from all lists and release + the file handle. + */ + + rc = gv_FlmSysData.pFileHdlMgr->Remove( pFileHdl); + iRefCnt = pFileHdl->Release(); + flmAssert( iRefCnt == 0); // pFileHdl should have been freed. + } + else + { + /* + Link out of the used list and move to the available list. + */ + + rc = gv_FlmSysData.pFileHdlMgr->MakeAvailAndRelease( pFileHdl); + + /* + NOTE: MakeAvailAndRelease will perform a release on the + file handle object for the caller. + */ + } + + clearCkoFileHdl( pCkoFileHdl); + } + + return( rc); +} + +/**************************************************************************** +Desc: Copy one CKO array into another. +****************************************************************************/ +void F_SuperFileHdl::copyCkoFileHdls( + CHECKED_OUT_FILE_HDL * pSrcCkoArray, + FLMUINT uiSrcHighestUsedSlot + ) +{ + FLMUINT uiNewSlot; + FLMUINT uiSrcSlot; + + // Zeroeth element is always copied. + + f_memcpy( m_pCheckedOutFileHdls, pSrcCkoArray, + sizeof( CHECKED_OUT_FILE_HDL)); + + // Memset the rest of the destination array to zero. + + f_memset( &m_pCheckedOutFileHdls[1], 0, sizeof( CHECKED_OUT_FILE_HDL) * + (m_uiCkoArraySize - 1)); + + m_uiHighestUsedSlot = 0; + m_uiLowestDirtySlot = 1; + m_uiHighestDirtySlot = 0; + for (uiSrcSlot = 1, pSrcCkoArray++; + uiSrcSlot <= uiSrcHighestUsedSlot; + uiSrcSlot++, pSrcCkoArray++) + { + if (pSrcCkoArray->pFileHdl && pSrcCkoArray->uiFileNumber) + { + uiNewSlot = pSrcCkoArray->uiFileNumber % (m_uiCkoArraySize - 1) + 1; + + // Only overwrite the destination one if the file number is + // lower than the one already there + + if (pSrcCkoArray->uiFileNumber < + m_pCheckedOutFileHdls [uiNewSlot].uiFileNumber || + !m_pCheckedOutFileHdls [uiNewSlot].uiFileNumber) + { + if (m_pCheckedOutFileHdls [uiNewSlot].uiFileNumber) + { + ReleaseFile( &m_pCheckedOutFileHdls [uiNewSlot], FALSE); + } + f_memcpy( &m_pCheckedOutFileHdls [uiNewSlot], pSrcCkoArray, + sizeof( CHECKED_OUT_FILE_HDL)); + if (uiNewSlot > m_uiHighestUsedSlot) + { + m_uiHighestUsedSlot = uiNewSlot; + } + if (m_uiHighestFileNumber < pSrcCkoArray->uiFileNumber) + { + m_uiHighestFileNumber = pSrcCkoArray->uiFileNumber; + } + if (pSrcCkoArray->bDirty) + { + if (m_uiLowestDirtySlot > m_uiHighestDirtySlot) + + { + m_uiLowestDirtySlot = + m_uiHighestDirtySlot = uiNewSlot; + } + else if( m_uiHighestDirtySlot < uiNewSlot) + { + m_uiHighestDirtySlot = uiNewSlot; + } + else if (m_uiLowestDirtySlot < uiNewSlot) + { + m_uiLowestDirtySlot = uiNewSlot; + } + } + } + else + { + ReleaseFile( pSrcCkoArray, FALSE); + } + } + } +} + +/**************************************************************************** +Desc: Disable flush minimizing. +****************************************************************************/ +void F_SuperFileHdl::disableFlushMinimize( void) +{ + + // Copy the allocated array back into the fixed array. + // This doesn't necessarily copy all of the file handles. + + if (m_pCheckedOutFileHdls != &m_CheckedOutFileHdls [0]) + { + CHECKED_OUT_FILE_HDL * pOldCkoArray = m_pCheckedOutFileHdls; + FLMUINT uiOldHighestUsedSlot = m_uiHighestUsedSlot; + + m_pCheckedOutFileHdls = &m_CheckedOutFileHdls [0]; + m_uiCkoArraySize = MAX_CHECKED_OUT_FILE_HDLS + 1; + copyCkoFileHdls( pOldCkoArray, uiOldHighestUsedSlot); + + f_free( &pOldCkoArray); + } + m_bMinimizeFlushes = FALSE; +} + +/**************************************************************************** +Desc: Flush dirty files to disk. +****************************************************************************/ +RCODE F_SuperFileHdl::Flush( void) +{ + RCODE rc = FERR_OK; + FLMUINT uiLoop; + + // Flush all dirty files + + for (uiLoop = m_uiLowestDirtySlot; + uiLoop <= m_uiHighestDirtySlot; + uiLoop++) + { + if( m_pCheckedOutFileHdls[ uiLoop].bDirty) + { + RCODE tmpRc; + + if (RC_BAD( tmpRc = + m_pCheckedOutFileHdls[ uiLoop].pFileHdl->Flush())) + { + rc = tmpRc; + ReleaseFile( &m_pCheckedOutFileHdls [uiLoop], TRUE); + } + m_pCheckedOutFileHdls[ uiLoop].bDirty = FALSE; + } + } + m_uiLowestDirtySlot = 1; + m_uiHighestDirtySlot = 0; + return( rc); +} + +/**************************************************************************** +Public: TruncateFile +Desc: Truncates back to an end of file block address. + This may only be called from reduce() because there cannot + be any other cases to reduce a 3x block file. +****************************************************************************/ +RCODE F_SuperFileHdl::TruncateFile( + FLMUINT uiEOFBlkAddress) +{ + RCODE rc = FERR_OK; + FLMUINT uiFileNumber = (FLMUINT)FSGetFileNumber( uiEOFBlkAddress); + FLMUINT uiBlockOffset = (FLMUINT)FSGetFileOffset( uiEOFBlkAddress); + F_FileHdlImp * pFileHdl; + + /* + Truncate the current block file. + */ + + if( RC_BAD( rc = GetFileHdl( uiFileNumber, TRUE, &pFileHdl))) + { + goto Exit; + } + + if( RC_BAD( rc = pFileHdl->Truncate( uiBlockOffset))) + { + ReleaseFile( uiFileNumber, TRUE); + goto Exit; + } + + /* + Visit the rest of the high block files until a NULL file hdl is hit. + */ + + for( ;;) + { + if( RC_BAD( GetFileHdl( ++uiFileNumber, TRUE, &pFileHdl))) + { + break; + } + + if( RC_BAD( rc = pFileHdl->Truncate( (FLMUINT)0 ))) + { + ReleaseFile( uiFileNumber, TRUE); + goto Exit; + } + + if( RC_BAD( rc = ReleaseFile( uiFileNumber, TRUE))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Public: TruncateFiles +Desc: Truncate to zero length any files between the specified start + and end files. +****************************************************************************/ +void F_SuperFileHdl::TruncateFiles( + FLMUINT uiStartFileNum, + FLMUINT uiEndFileNum) +{ + FLMUINT uiFileNumber; + F_FileHdlImp * pFileHdl; + + for( uiFileNumber = uiStartFileNum; + uiFileNumber <= uiEndFileNum; + uiFileNumber++ ) + { + if( RC_OK( GetFileHdl( uiFileNumber, TRUE, &pFileHdl ))) + { + (void)pFileHdl->Truncate( (FLMUINT)0 ); + (void)ReleaseFile( uiFileNumber, TRUE); + } + } +} + +/**************************************************************************** +Public: GetFileSize +Desc: Returns the physical size of a file +****************************************************************************/ +RCODE F_SuperFileHdl::GetFileSize( + FLMUINT uiFileNumber, + FLMUINT * puiFileSize) +{ + F_FileHdlImp * pFileHdl = NULL; + RCODE rc = FERR_OK; + + flmAssert( m_bSetupCalled); + flmAssert( puiFileSize); + + /* + Initialize the size to zero. + */ + + *puiFileSize = 0; + + /* + Get the file handle. + */ + + if( RC_BAD( rc = GetFileHdl( uiFileNumber, FALSE, &pFileHdl))) + { + goto Exit; + } + + if( RC_BAD( rc = pFileHdl->Size( puiFileSize))) + { + ReleaseFile( uiFileNumber, TRUE); + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Public: GetFilePath +Desc: Returns the path of a file given its file number +****************************************************************************/ +RCODE F_SuperFileHdl::GetFilePath( + FLMUINT uiFileNumber, + char * pszIoPath) +{ + RCODE rc = FERR_OK; + FLMUINT uiExtOffset; + + // Sanity checks + + flmAssert( m_bSetupCalled); + + if (!uiFileNumber) + { + f_strcpy( pszIoPath, m_pszDbFileName); + goto Exit; + } + + if ((m_uiDbVersion >= FLM_VER_4_3 && + uiFileNumber <= MAX_DATA_FILE_NUM_VER43) || + uiFileNumber <= MAX_DATA_FILE_NUM_VER40) + { + f_memcpy( pszIoPath, m_pszDataFileNameBase, m_uiDataExtOffset); + uiExtOffset = m_uiDataExtOffset; + } + else + { + f_memcpy( pszIoPath, m_pszDbFileName, m_uiExtOffset); + uiExtOffset = m_uiExtOffset; + } + + // Modify the file's extension. + + bldSuperFileExtension( m_uiDbVersion, + uiFileNumber, &pszIoPath[ uiExtOffset]); + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Reallocates the checked out file handle array. +****************************************************************************/ +RCODE F_SuperFileHdl::reallocCkoArray( + FLMUINT uiFileNum + ) +{ + RCODE rc = FERR_OK; + FLMUINT uiNewSize; + CHECKED_OUT_FILE_HDL * pNewCkoArray; + CHECKED_OUT_FILE_HDL * pOldCkoArray; + FLMUINT uiOldHighestUsedSlot; + + if (uiFileNum < m_uiHighestFileNumber) + { + uiFileNum = m_uiHighestFileNumber; + } + uiNewSize = uiFileNum + 128; + + // Reallocate so we can guarantee that all of the current file + // numbers will copy and there is room for this new one as well. + + if (uiNewSize > MAX_LOG_BLOCK_FILE_NUMBER( m_uiDbVersion) + 1) + { + flmAssert( uiFileNum <= MAX_LOG_BLOCK_FILE_NUMBER( m_uiDbVersion)); + uiNewSize = MAX_LOG_BLOCK_FILE_NUMBER( m_uiDbVersion) + 1; + } + + // No need to call f_calloc, because copyCkoFileHdls will initialize + // it below. + + if (RC_BAD( rc = f_alloc( sizeof( CHECKED_OUT_FILE_HDL) * uiNewSize, + &pNewCkoArray))) + { + goto Exit; + } + + pOldCkoArray = m_pCheckedOutFileHdls; + uiOldHighestUsedSlot = m_uiHighestUsedSlot; + + m_pCheckedOutFileHdls = pNewCkoArray; + m_uiCkoArraySize = uiNewSize; + + copyCkoFileHdls( pOldCkoArray, uiOldHighestUsedSlot); + + // Can't free the old one until after the copy! + + if (pOldCkoArray != &m_CheckedOutFileHdls [0]) + { + f_free( &pOldCkoArray); + } + +Exit: + + return( rc); + +} + +/**************************************************************************** +Public: GetFileHdl +Desc: Returns a file handle given the file's number +****************************************************************************/ +RCODE F_SuperFileHdl::GetFileHdl( + FLMUINT uiFileNum, + FLMBOOL bGetForUpdate, + F_FileHdlImp ** ppFileHdl) +{ + RCODE rc = FERR_OK; + F_FileHdlImp * pFileHdl = NULL; + FLMUINT uiFileId; + CHECKED_OUT_FILE_HDL * pCkoFileHdl; + char szFilePath[ F_PATH_MAX_SIZE]; + FLMUINT uiSlot; + + // Get the file handle + + pCkoFileHdl = getCkoFileHdlPtr( uiFileNum, &uiSlot); + if( pCkoFileHdl->uiFileNumber != uiFileNum && + pCkoFileHdl->pFileHdl) + { + if (pCkoFileHdl->bDirty && m_bMinimizeFlushes) + { + flmAssert( pCkoFileHdl->uiFileNumber); + if (RC_BAD( reallocCkoArray( uiFileNum))) + { + goto Exit; + } + pCkoFileHdl = getCkoFileHdlPtr( uiFileNum, &uiSlot); + + // Better have reallocated so that the new slot for + // the file number has nothing in it. + + flmAssert( !pCkoFileHdl->uiFileNumber && + !pCkoFileHdl->pFileHdl); + } + else + { + if( RC_BAD( rc = ReleaseFile( pCkoFileHdl, FALSE))) + { + goto Exit; + } + } + } + + if( !pCkoFileHdl->pFileHdl) + { + // Get the file ID + + if( RC_BAD( rc = m_pFileIdList->getFileId( uiFileNum, &uiFileId))) + { + goto Exit; + } + + // Look for an available file handle if not opening exclusive. + // NOTE: AddRef() performed for caller by FindAvail if a file + // handle is found. + + if( RC_BAD( gv_FlmSysData.pFileHdlMgr->FindAvail( + uiFileId, FALSE, &pFileHdl))) + { + // Allocate a new file handle, open the file and + // link into the used directory. + + if( (pFileHdl = f_new F_FileHdlImp) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + +#ifdef FLM_WIN + /* + If m_uiBlockSize is 0, direct I/O will not be used + */ + + pFileHdl->SetBlockSize( m_uiBlockSize); +#endif + + flmAssert( uiFileId); // File ID must be non-zero + + if( RC_BAD( rc = pFileHdl->Setup( uiFileId))) + { + goto Exit; + } + + // Build the file path + + if( RC_BAD( rc = GetFilePath( uiFileNum, szFilePath))) + { + goto Exit; + } + + // Open the file + + if( RC_BAD( rc = pFileHdl->Open( szFilePath, + F_IO_RDWR | F_IO_SH_DENYNONE | F_IO_DIRECT))) + { + goto Exit; + } + + // Insert into the manager + + if( RC_BAD( rc = gv_FlmSysData.pFileHdlMgr->InsertNew( pFileHdl))) + { + goto Exit; + } + } + + pCkoFileHdl->pFileHdl = pFileHdl; + pFileHdl = NULL; // Set to NULL so the handle won't be released below + pCkoFileHdl->uiFileNumber = uiFileNum; + pCkoFileHdl->bDirty = FALSE; + if( m_uiHighestUsedSlot < uiSlot) + { + m_uiHighestUsedSlot = uiSlot; + } + if (m_uiHighestFileNumber < uiFileNum) + { + m_uiHighestFileNumber = uiFileNum; + } + } + + *ppFileHdl = pCkoFileHdl->pFileHdl; + if( bGetForUpdate) + { + pCkoFileHdl->bDirty = TRUE; + if (m_uiLowestDirtySlot > m_uiHighestDirtySlot) + + { + m_uiLowestDirtySlot = + m_uiHighestDirtySlot = uiSlot; + } + else if( m_uiHighestDirtySlot < uiSlot) + { + m_uiHighestDirtySlot = uiSlot; + } + else if (m_uiLowestDirtySlot < uiSlot) + { + m_uiLowestDirtySlot = uiSlot; + } + } + +Exit: + + if( pFileHdl) + { + pFileHdl->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: Generates a file name given a super file number. + Adds ".xx" to pFileExtension. Use lower case characters. +Notes: This is a base 24 alphanumeric value where + { a, b, c, d, e, f, i, l, o, r, u, v } values are removed. +****************************************************************************/ +void bldSuperFileExtension( + FLMUINT uiDbVersion, + FLMUINT uiFileNum, + char * pszFileExtension) +{ + FLMBYTE ucLetter; + + flmAssert( uiDbVersion); // Make sure the database version is valid + + if (uiDbVersion >= FLM_VER_4_3) + { + if (uiFileNum <= MAX_DATA_FILE_NUM_VER43 - 1536) + { + // No additional letter - File numbers 1 to 511 + // This is just like pre-4.3 numbering. + ucLetter = 0; + } + else if (uiFileNum <= MAX_DATA_FILE_NUM_VER43 - 1024) + { + // File numbers 512 to 1023 + ucLetter = 'r'; + } + else if (uiFileNum <= MAX_DATA_FILE_NUM_VER43 - 512) + { + // File numbers 1024 to 1535 + ucLetter = 's'; + } + else if (uiFileNum <= MAX_DATA_FILE_NUM_VER43) + { + // File numbers 1536 to 2047 + ucLetter = 't'; + } + else if (uiFileNum <= MAX_LOG_FILE_NUM_VER43 - 1536) + { + // File numbers 2048 to 2559 + ucLetter = 'v'; + } + else if (uiFileNum <= MAX_LOG_FILE_NUM_VER43 - 1024) + { + // File numbers 2560 to 3071 + ucLetter = 'w'; + } + else if (uiFileNum <= MAX_LOG_FILE_NUM_VER43 - 512) + { + // File numbers 3072 to 3583 + ucLetter = 'x'; + } + else + { + flmAssert( uiFileNum <= MAX_LOG_FILE_NUM_VER43); + + // File numbers 3584 to 4095 + ucLetter = 'z'; + } + } + else // Pre-4.3 versions + { + if (uiFileNum <= MAX_DATA_FILE_NUM_VER40) + { + // No additional letter - File numbers 1 to 511 + // This is just like pre-4.3 numbering. + ucLetter = 0; + } + else + { + flmAssert( uiFileNum <= MAX_LOG_FILE_NUM_VER40); + + // File numbers 512 to 1023 + ucLetter = 'x'; + } + } + + *pszFileExtension++ = '.'; + *pszFileExtension++ = base24ToDigit( (uiFileNum & 511) / 24); + *pszFileExtension++ = base24ToDigit( (uiFileNum & 511) % 24); + *pszFileExtension++ = ucLetter; + *pszFileExtension = 0; +} + +/**************************************************************************** +Desc: Turn a base 24 value into a native alphanumeric value. +Notes: This is a base 24 alphanumeric value where + {a, b, c, d, e, f, i, l, o, r, u, v } values are removed. +****************************************************************************/ +FSTATIC FLMBYTE base24ToDigit( + FLMUINT uiValue) +{ + flmAssert( uiValue <= 23); + + if( uiValue <= 9) + { + uiValue += (FLMUINT) NATIVE_ZERO; + } + else + { + uiValue = f_toascii(uiValue) - 10 + (FLMUINT)f_toascii('g'); + if( uiValue >= (FLMUINT)'i') + { + uiValue++; + if( uiValue >= (FLMUINT)'l') + { + uiValue++; + if( uiValue >= (FLMUINT)'o') + { + uiValue++; + if( uiValue >= (FLMUINT)'r') + { + uiValue++; + if( uiValue >= (FLMUINT)'u') + { + uiValue++; + if( uiValue >= (FLMUINT)'v') + { + uiValue++; + } + } + } + } + } + } + } + return (FLMBYTE)uiValue; +} diff --git a/flaim/src/fsysdata.cpp b/flaim/src/fsysdata.cpp index 0e64a79..4fd2103 100644 --- a/flaim/src/fsysdata.cpp +++ b/flaim/src/fsysdata.cpp @@ -1,5246 +1,5251 @@ -//------------------------------------------------------------------------- -// Desc: Initialization and shutdown and system data. -// Tabs: 3 -// -// Copyright (c) 1995-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: fsysdata.cpp 12334 2006-01-23 12:45:35 -0700 (Mon, 23 Jan 2006) dsanders $ -//------------------------------------------------------------------------- - -#define ALLOCATE_SYS_DATA 1 - -#include "flaimsys.h" - -#ifdef FLM_AIX - #include -#endif -#ifdef FLM_HPUX - #include - #include - #include -#endif - -#if defined( FLM_WIN) || defined( FLM_NLM) - #define FLM_CAN_GET_PHYS_MEM -#elif defined( FLM_UNIX) - #if defined( _SC_AVPHYS_PAGES) || defined( FLM_HPUX) - #define FLM_CAN_GET_PHYS_MEM - #endif -#endif - -#define FLM_MIN_FREE_BYTES (2 * 1024 * 1024) - -#ifdef FLM_32BIT - #if defined( FLM_LINUX) - - // With mmap'd memory on Linux, you're effectively limited to about ~2 GB. - // Userspace only gets ~3GB of useable address space anyway, and then you - // have all of the thread stacks too, which you can't have - // overlapping the heap. - - #define FLM_MAX_CACHE_SIZE (1500 * 1024 * 1024) - #else - #define FLM_MAX_CACHE_SIZE (2000 * 1024 * 1024) - #endif -#else - #define FLM_MAX_CACHE_SIZE (~((FLMUINT)0)) -#endif - - -FLMUINT32 gv_ui32FlmSysSpinLock = 0; -FLMUINT gv_uiFlmSysStartupCount = 0; -FLMBOOL gv_bNetWareStartupCalled = FALSE; - -#ifdef FLM_NLM - extern "C" - { - void flmHttpConfig( - FLMBOOL bEnable, - const char * pszParams); - } -#endif - -FSTATIC void flmInitHashTbl( - FBUCKET * pHashTable, - FLMUINT uiHashEntries, - f_randomGenerator * RandGen); - -#ifdef FLM_CAN_GET_PHYS_MEM -FSTATIC FLMUINT flmGetCacheBytes( - FLMUINT uiPercent, - FLMUINT uiMin, - FLMUINT uiMax, - FLMUINT uiMinToLeave, - FLMBOOL bCalcOnAvailMem, - FLMUINT uiBytesCurrentlyInUse); -#endif - -FSTATIC void flmLockSysData( void); - -FSTATIC void flmUnlockSysData( void); - -FSTATIC RCODE flmSetCacheLimits( - FLMUINT uiNewTotalCacheSize, - FLMBOOL bPreallocateCache); - -FSTATIC void flmFreeEvent( - FEVENT * pEvent, - F_MUTEX hMutex, - FEVENT ** ppEventListRV); - -FSTATIC RCODE flmCloseDbFile( - const char * pszDbFileName, - const char * pszDataDir); - -FSTATIC void flmShutdownDbThreads( - FFILE * pFile); - -FSTATIC void flmCleanup( void); - -FSTATIC void flmUnlinkFileFromBucket( - FFILE * pFile); - -RCODE flmMonitor( - F_Thread * pThread); - -FSTATIC RCODE flmRegisterHttpCallback( - FLM_MODULE_HANDLE hModule, - const char * pszUrlString); - -FSTATIC RCODE flmDeregisterHttpCallback( void); - -/**************************************************************************** -Desc: This routine frees all of the notify requests in a list of requests. -****************************************************************************/ -FINLINE void flmFreeNotifyList( - FNOTIFY ** ppNotifyRV) -{ - FNOTIFY * pTmp; - FNOTIFY * pNotify = *ppNotifyRV; - - while (pNotify) - { - f_semDestroy( &pNotify->hSem); - pTmp = pNotify; - pNotify = pNotify->pNext; - (void)f_free( &pTmp); - } - - *ppNotifyRV = NULL; -} - -/**************************************************************************** -Desc: Sets the path for all temporary files that come into use within a - FLAIM share structure. The share mutex should be locked when - settting when called from FlmConfig(). -****************************************************************************/ -FINLINE RCODE flmSetTmpDir( - const char * pszTmpDir) -{ - RCODE rc; - - if( RC_BAD( rc = gv_FlmSysData.pFileSystem->Exists( pszTmpDir))) - { - goto Exit; - } - - f_strcpy( gv_FlmSysData.szTempDir, pszTmpDir); - gv_FlmSysData.bTempDirSet = TRUE; - -Exit: - - return( rc); -} - -/**************************************************************************** -Desc: This routine frees all of the local dictionaries in a list of - local dictionaries. -****************************************************************************/ -FINLINE void flmFreeDictList( - FDICT ** ppDictRV) -{ - FDICT * pTmp; - FDICT * pDict = *ppDictRV; - - while (pDict) - { - pTmp = pDict; - pDict = pDict->pNext; - flmFreeDict( pTmp); - } - - *ppDictRV = NULL; -} - -/**************************************************************************** -Desc: This routine initializes a hash table. -****************************************************************************/ -FSTATIC void flmInitHashTbl( - FBUCKET * pHashTable, - FLMUINT uiHashEntries, - f_randomGenerator * RandGen) -{ - FLMUINT uiCnt; - FLMUINT uiRandVal; - FLMUINT uiTempVal; - - for( uiCnt = 0; uiCnt < uiHashEntries; uiCnt++) - { - pHashTable [uiCnt].uiHashValue = (FLMBYTE)uiCnt; - pHashTable [uiCnt].pFirstInBucket = NULL; - } - - if (uiHashEntries <= 256) - { - for (uiCnt = 0; uiCnt < uiHashEntries - 1; uiCnt++) - { - uiRandVal = (FLMBYTE) f_randomChoice( RandGen, (FLMINT32)uiCnt, - (FLMINT32)(uiHashEntries - 1)); - if (uiRandVal != uiCnt) - { - uiTempVal = (FLMBYTE)pHashTable [uiCnt].uiHashValue; - pHashTable [uiCnt].uiHashValue = pHashTable [uiRandVal].uiHashValue; - pHashTable [uiRandVal].uiHashValue = uiTempVal; - } - } - } -} - -/**************************************************************************** -Desc: This routine allocates and initializes a hash table. -****************************************************************************/ -RCODE flmAllocHashTbl( - FLMUINT uiHashTblSize, - FBUCKET ** ppHashTblRV) -{ - RCODE rc = FERR_OK; - f_randomGenerator RandGen; - FBUCKET * pHashTbl = NULL; - - // Allocate memory for the hash table - - if( RC_BAD( rc = f_calloc( - (FLMUINT)(sizeof( FBUCKET)) * uiHashTblSize, &pHashTbl))) - { - goto Exit; - } - - // Initialize the hash table - - f_randomSetSeed( &RandGen, 1); - flmInitHashTbl( pHashTbl, uiHashTblSize, &RandGen); - -Exit: - - *ppHashTblRV = pHashTbl; - return( rc); -} - -/**************************************************************************** -Desc: This routine determines the number of cache bytes to use for caching - based on a percentage of available physical memory or a percentage - of physical memory (depending on bCalcOnAvailMem flag). - uiBytesCurrentlyInUse indicates how many bytes are currently allocated - by FLAIM - so it can factor that in if the calculation is to be based - on the available memory. - Lower limit is 1 megabyte. -****************************************************************************/ -#ifdef FLM_CAN_GET_PHYS_MEM -FSTATIC FLMUINT flmGetCacheBytes( - FLMUINT uiPercent, - FLMUINT uiMin, - FLMUINT uiMax, - FLMUINT uiMinToLeave, - FLMBOOL bCalcOnAvailMem, - FLMUINT uiBytesCurrentlyInUse - ) -{ - FLMUINT uiMem; -#if defined( FLM_WIN) - MEMORYSTATUS MemStatus; -#elif defined( FLM_UNIX) - FLMUINT uiProcMemLimit = FLM_MAX_UINT; - FLMUINT uiProcVMemLimit = FLM_MAX_UINT; -#endif - -#if defined( FLM_WIN) - GlobalMemoryStatus( &MemStatus); - uiMem = (FLMUINT)((bCalcOnAvailMem) - ? (FLMUINT)MemStatus.dwAvailPhys - : (FLMUINT)MemStatus.dwTotalPhys); -#elif defined( FLM_UNIX) - { -#ifdef FLM_AIX - struct vminfo tmpvminfo; - #ifdef _SC_PAGESIZE - long iPageSize = sysconf(_SC_PAGESIZE); - #else - long iPageSize = 4096; - #endif - - if( iPageSize == -1) - { - // If sysconf returned an error, resort to using the default - // page size for the Power architecture. - - iPageSize = 4096; - } - - uiMem = FLM_MAX_UINT; - if( vmgetinfo( &tmpvminfo, VMINFO, sizeof( tmpvminfo)) != -1) - { - if( bCalcOnAvailMem) - { - if( tmpvminfo.numfrb < FLM_MAX_UINT) - { - uiMem = (FLMUINT)tmpvminfo.numfrb; - } - } - else - { - if( tmpvminfo.memsizepgs < FLM_MAX_UINT) - { - uiMem = (FLMUINT)tmpvminfo.memsizepgs; - } - } - } -#elif defined( FLM_HPUX) - long iPageSize; - struct pst_static pst; - - if (pstat_getstatic( &pst, sizeof( pst), (size_t)1, 0) == -1) - { - iPageSize = 4096; - } - else - { - iPageSize = pst.page_size; - } - if (bCalcOnAvailMem) - { - struct pst_dynamic dyn; - - if (pstat_getdynamic( &dyn, sizeof( dyn), 1, 0) != -1) - { - uiMem = (FLMUINT)dyn.psd_free; - } - else - { - uiMem = 0; - } - } - else - { - uiMem = (FLMUINT)pst.physical_memory; - } -#else - - long iPageSize = sysconf(_SC_PAGESIZE); - - // Get the amount of memory available to the system - - uiMem = (FLMUINT)((bCalcOnAvailMem) - ? (FLMUINT)sysconf(_SC_AVPHYS_PAGES) - : (FLMUINT)sysconf(_SC_PHYS_PAGES)); -#endif - - if (FLM_MAX_UINT / (FLMUINT)iPageSize >= uiMem) - { - uiMem *= (FLMUINT)iPageSize; - } - else - { - uiMem = FLM_MAX_UINT; - } - - #if defined( RLIMIT_VMEM) - // Bump the process soft virtual limit up to the hard limit - { - struct rlimit rlim; - - if( getrlimit( RLIMIT_VMEM, &rlim) == 0) - { - if( rlim.rlim_cur < rlim.rlim_max) - { - rlim.rlim_cur = rlim.rlim_max; - (void)setrlimit( RLIMIT_VMEM, &rlim); - if( getrlimit( RLIMIT_VMEM, &rlim) != 0) - { - rlim.rlim_cur = RLIM_INFINITY; - rlim.rlim_max = RLIM_INFINITY; - } - } - - if( rlim.rlim_cur != RLIM_INFINITY) - { - uiProcVMemLimit = (FLMUINT)rlim.rlim_cur; - } - } - } - #endif - - #if defined( RLIMIT_DATA) - - // Bump the process soft heap limit up to the hard limit - { - struct rlimit rlim; - - if( getrlimit( RLIMIT_DATA, &rlim) == 0) - { - if( rlim.rlim_cur < rlim.rlim_max) - { - rlim.rlim_cur = rlim.rlim_max; - (void)setrlimit( RLIMIT_DATA, &rlim); - if( getrlimit( RLIMIT_DATA, &rlim) != 0) - { - rlim.rlim_cur = RLIM_INFINITY; - rlim.rlim_max = RLIM_INFINITY; - } - } - - if( rlim.rlim_cur != RLIM_INFINITY) - { - uiProcMemLimit = (FLMUINT)rlim.rlim_cur; - } - } - } - - #endif - } -#elif defined( FLM_NLM) - { - FLMUINT uiCacheBufferSize = GetCacheBufferSize(); - - uiMem = (FLMUINT)((bCalcOnAvailMem) - ? (FLMUINT)GetCurrentNumberOfCacheBuffers() - : (FLMUINT)GetOriginalNumberOfCacheBuffers()); - - // Operating System will never give up last three hundred buffers. - - if (uiMem > 300) - { - uiMem -= 300; - } - else - { - uiMem = 0; - } - if (uiMem > FLM_MAX_UINT / uiCacheBufferSize) - { - uiMem = FLM_MAX_UINT; - } - else - { - uiMem *= uiCacheBufferSize; - } - - // Get available memory in local process pool - - if (bCalcOnAvailMem) - { - FLMUINT uiFreeBytes; - FLMUINT uiFreeNodes; - FLMUINT uiAllocatedBytes; - FLMUINT uiAllocatedNodes; - FLMUINT uiTotalMemory; - - if (GetNLMAllocMemoryCounts( f_getNLMHandle(), - &uiFreeBytes, &uiFreeNodes, - &uiAllocatedBytes, &uiAllocatedNodes, - &uiTotalMemory) == 0) - { - if (uiMem > FLM_MAX_UINT - uiFreeBytes) - { - uiMem = FLM_MAX_UINT; - } - else - { - uiMem += uiFreeBytes; - } - } - } - } -#else - #error Getting physical memory is not supported by this platform. -#endif - - // If we are basing the calculation on available physical memory, - // take in to account what has already been allocated. - - if (bCalcOnAvailMem) - { - if (uiMem > FLM_MAX_UINT - uiBytesCurrentlyInUse) - { - uiMem = FLM_MAX_UINT; - } - else - { - uiMem += uiBytesCurrentlyInUse; - } - } - - // Determine if there are limits on the amount of memory the - // process can access and reset uiMem accordingly. There may - // be more available memory than the process is able to access. - -#ifdef FLM_WIN - - // There could be more physical memory in the system than we could - // actually allocate in our virtual address space. Thus, we need to - // make sure that we never exceed our total virtual address space. - - if (uiMem > (FLMUINT)MemStatus.dwTotalVirtual) - { - uiMem = (FLMUINT)MemStatus.dwTotalVirtual; - } - -#elif defined( FLM_UNIX) - - // The process might be limited in the amount of memory it - // can access. - - if ( uiMem > uiProcMemLimit) - { - uiMem = uiProcMemLimit; - } - - if( uiMem > uiProcVMemLimit) - { - uiMem = uiProcVMemLimit; - } - -#endif - - // If uiMax is zero, use uiMinToLeave to calculate the maximum. - - if (!uiMax) - { - if (!uiMinToLeave) - { - uiMax = uiMem; - } - else if (uiMinToLeave < uiMem) - { - uiMax = uiMem - uiMinToLeave; - } - else - { - uiMax = 0; - } - } - - // Calculate memory as a percentage of memory. - - uiMem = (FLMUINT)((uiMem > FLM_MAX_UINT / 100) - ? (FLMUINT)(uiMem / 100) * uiPercent - : (FLMUINT)(uiMem * uiPercent) / 100); - - // Don't go above the maximum. - - if (uiMem > uiMax) - { - uiMem = uiMax; - } - - // Don't go below the minimum. - - if (uiMem < uiMin) - { - uiMem = uiMin; - } - return( uiMem); -} -#endif - -/*************************************************************************** -Desc: Lock the system data structure for access - called only by startup - and shutdown. NOTE: On platforms that do not support atomic exchange - this is less than perfect - won't handle tight race conditions. -***************************************************************************/ -FSTATIC void flmLockSysData( void) -{ -#ifdef ATOMIC_INCDEC_SUPPORT - - // Obtain the spin lock - - while (ftkAtomicExchange( &gv_ui32FlmSysSpinLock, 1) == 1) - { - f_sleep( 10); - } -#else - while (gv_ui32FlmSysSpinLock) - { - f_sleep( 10); - } - gv_ui32FlmSysSpinLock = 1; -#endif -} - -/*************************************************************************** -Desc: Unlock the system data structure for access - called only by startup - and shutdown. -***************************************************************************/ -FSTATIC void flmUnlockSysData( void) -{ -#ifdef ATOMIC_INCDEC_SUPPORT - (void)ftkAtomicExchange( &gv_ui32FlmSysSpinLock, 0); -#else - gv_ui32FlmSysSpinLock = 0; -#endif -} - -/*API~*********************************************************************** -Desc : Startup FLAIM. -Notes: This routine may be called multiple times. However, if that is done - FlmShutdown() should be called for each time this is called - successfully. This routine does not handle race conditions on - platforms that do not support atomic increment. -*END************************************************************************/ -FLMEXP RCODE FLMAPI FlmStartup( void) -{ - RCODE rc = FERR_OK; - FLMINT iEventCategory; - FLMUINT uiCacheBytes; -#ifdef FLM_USE_NICI - int iHandle; -#endif - - flmLockSysData(); - - // See if FLAIM has already been started. If so, - // we are done. - - if (++gv_uiFlmSysStartupCount > 1) - { - goto Exit; - } - -#ifdef FLM_NLM - gv_bNetWareStartupCalled = TRUE; - if( RC_BAD( rc = f_netwareStartup())) - { - goto Exit; - } -#endif - - // Sanity check -- make sure we are using the correct - // byte-swap macros for this platform - - flmAssert( FB2UD( "\x0A\x0B\x0C\x0D") == 0x0D0C0B0A); - flmAssert( FB2UW( "\x0A\x0B") == 0x0B0A); - - // The memset needs to be first. - - f_memset( &gv_FlmSysData, 0, sizeof( FLMSYSDATA)); - -#if defined( FLM_LINUX) - flmGetLinuxKernelVersion( &gv_FlmSysData.uiLinuxMajorVer, - &gv_FlmSysData.uiLinuxMinorVer, - &gv_FlmSysData.uiLinuxRevision); - gv_FlmSysData.uiMaxFileSize = flmGetLinuxMaxFileSize( sizeof( FLMUINT)); -#elif defined( FLM_AIX) - // Call set setrlimit to increase the max allowed file size. - // We don't have a good way to deal with any errors returned by - // setrlimit(), so we just hope that there aren't any... - struct rlimit rlim; - rlim.rlim_cur = RLIM_INFINITY; - rlim.rlim_max = RLIM_INFINITY; - setrlimit( RLIMIT_FSIZE, &rlim); - gv_FlmSysData.uiMaxFileSize = F_MAXIMUM_FILE_SIZE; -#else - gv_FlmSysData.uiMaxFileSize = F_MAXIMUM_FILE_SIZE; -#endif - - flmAssert( gv_FlmSysData.uiMaxFileSize); - - // Initialize memory tracking variables - should be done before - // call to f_memoryInit(). - -#ifdef FLM_DEBUG - - // Variables for memory allocation tracking. - - gv_FlmSysData.bTrackLeaks = TRUE; - gv_FlmSysData.hMemTrackingMutex = F_MUTEX_NULL; -#ifdef DEBUG_SIM_OUT_OF_MEM - f_randomSetSeed( &gv_FlmSysData.memSimRandomGen, 1); -#endif -#endif - - gv_FlmSysData.hShareMutex = F_MUTEX_NULL; - gv_FlmSysData.hFileHdlMutex = F_MUTEX_NULL; - gv_FlmSysData.uiMaxStratifyIterations = DEFAULT_MAX_STRATIFY_ITERATIONS; - gv_FlmSysData.uiMaxStratifyTime = DEFAULT_MAX_STRATIFY_TIME; - - // Initialize the event categories to have no mutex. - - for (iEventCategory = 0; - iEventCategory < F_MAX_EVENT_CATEGORIES; - iEventCategory++) - { - gv_FlmSysData.EventHdrs [iEventCategory].hMutex = F_MUTEX_NULL; - } - - // Memory initialization should be first. - - f_memoryInit(); - -#if defined( FLM_NLM) || (defined( FLM_WIN) && !defined( FLM_64BIT)) - /* Initialize the checksum code variable. */ - InitFastBlockCheckSum(); -#endif - -#if defined( FLM_NLM) - if (RC_BAD( rc = nssInitialize())) - { - goto Exit; - } -#endif - -#ifdef FLM_DBG_LOG - flmDbgLogInit(); -#endif - - /* Initialize all of the fields. */ - - FLM_SECS_TO_TIMER_UNITS( DEFAULT_MAX_UNUSED_TIME, - gv_FlmSysData.uiMaxUnusedTime); - FLM_SECS_TO_TIMER_UNITS( DEFAULT_MAX_CP_INTERVAL, - gv_FlmSysData.uiMaxCPInterval); - FLM_SECS_TO_TIMER_UNITS( DEFAULT_MAX_TRANS_SECS, - gv_FlmSysData.uiMaxTransTime); - FLM_SECS_TO_TIMER_UNITS( DEFAULT_MAX_TRANS_INACTIVE_SECS, - gv_FlmSysData.uiMaxTransInactiveTime); - -#ifdef FLM_CAN_GET_PHYS_MEM - gv_FlmSysData.bDynamicCacheAdjust = TRUE; - gv_FlmSysData.uiCacheAdjustPercent = DEFAULT_CACHE_ADJUST_PERCENT; - gv_FlmSysData.uiCacheAdjustMin = DEFAULT_CACHE_ADJUST_MIN; - gv_FlmSysData.uiCacheAdjustMax = DEFAULT_CACHE_ADJUST_MAX; - gv_FlmSysData.uiCacheAdjustMinToLeave = DEFAULT_CACHE_ADJUST_MIN_TO_LEAVE; - FLM_SECS_TO_TIMER_UNITS( DEFAULT_CACHE_ADJUST_INTERVAL, - gv_FlmSysData.uiCacheAdjustInterval); - uiCacheBytes = flmGetCacheBytes( gv_FlmSysData.uiCacheAdjustPercent, - gv_FlmSysData.uiCacheAdjustMin, - gv_FlmSysData.uiCacheAdjustMax, - gv_FlmSysData.uiCacheAdjustMinToLeave, TRUE, 0); -#else - gv_FlmSysData.bDynamicCacheAdjust = FALSE; - gv_FlmSysData.uiCacheAdjustInterval = 0; - uiCacheBytes = DEFAULT_CACHE_ADJUST_MIN; -#endif - - if( uiCacheBytes > FLM_MAX_CACHE_SIZE) - { - uiCacheBytes = FLM_MAX_CACHE_SIZE; - } - - gv_FlmSysData.uiBlockCachePercentage = DEFAULT_BLOCK_CACHE_PERCENTAGE; - - FLM_SECS_TO_TIMER_UNITS( DEFAULT_CACHE_CLEANUP_INTERVAL, - gv_FlmSysData.uiCacheCleanupInterval); - FLM_SECS_TO_TIMER_UNITS( DEFAULT_UNUSED_CLEANUP_INTERVAL, - gv_FlmSysData.uiUnusedCleanupInterval); - - // Initialize the thread manager - - if( (gv_FlmSysData.pThreadMgr = f_new F_ThreadMgr) == NULL) - { - rc = RC_SET( FERR_MEM); - goto Exit; - } - - if( RC_BAD( rc = gv_FlmSysData.pThreadMgr->setupThreadMgr())) - { - goto Exit; - } - - // Initialize the serial number generator - - if( RC_BAD( rc = f_initSerialNumberGenerator())) - { - goto Exit; - } - - // Initialize the slab manager - - if( (gv_FlmSysData.pSlabManager = f_new F_SlabManager) == NULL) - { - rc = RC_SET( FERR_MEM); - goto Exit; - } - - if( !gv_FlmSysData.bDynamicCacheAdjust) - { - if( RC_BAD( rc = gv_FlmSysData.pSlabManager->setup( uiCacheBytes))) - { - goto Exit; - } - } - else - { - if( RC_BAD( rc = gv_FlmSysData.pSlabManager->setup( 0))) - { - goto Exit; - } - } - - // Divide cache bytes evenly between block and record cache. - - gv_FlmSysData.uiMaxCache = uiCacheBytes; - uiCacheBytes >>= 1; - if (RC_BAD( rc = ScaInit( uiCacheBytes))) - { - goto Exit; - } - - if (RC_BAD( rc = flmRcaInit( uiCacheBytes))) - { - goto Exit; - } - - /* Create the mutex for controlling access to the structure. */ - - if (RC_BAD( rc = f_mutexCreate( &gv_FlmSysData.hShareMutex))) - { - goto Exit; - } - - if (RC_BAD( rc = f_mutexCreate( &gv_FlmSysData.hQueryMutex))) - { - goto Exit; - } - - if (RC_BAD(rc = f_mutexCreate( &gv_FlmSysData.hFileHdlMutex))) - { - goto Exit; - } - - if (RC_BAD(rc = f_mutexCreate( &gv_FlmSysData.hServerLockMgrMutex))) - { - goto Exit; - } - - if (RC_BAD( rc = f_mutexCreate( &gv_FlmSysData.HttpConfigParms.hMutex))) - { - goto Exit; - } - - if (RC_BAD( rc = f_mutexCreate( &gv_FlmSysData.hHttpSessionMutex))) - { - goto Exit; - } - - /* Initialize a statistics structure. */ - - if (RC_BAD( rc = flmStatInit( &gv_FlmSysData.Stats, TRUE))) - { - goto Exit; - } - gv_FlmSysData.bStatsInitialized = TRUE; - - /* Allocate memory for the file name hash table. */ - - if (RC_BAD(rc = flmAllocHashTbl( FILE_HASH_ENTRIES, - &gv_FlmSysData.pFileHashTbl))) - { - goto Exit; - } - - gv_FlmSysData.uiNextFFileId = 1; - - /* Allocate and Initialize FLAIM Shared File Handle Manager */ - - if ((gv_FlmSysData.pFileHdlMgr = - new F_FileHdlMgr( &gv_FlmSysData.hFileHdlMutex)) == NULL) - { - rc = RC_SET( FERR_MEM); - goto Exit; - } - - if (RC_BAD( rc = gv_FlmSysData.pFileHdlMgr->Setup( DEFAULT_OPEN_THRESHOLD, - DEFAULT_MAX_UNUSED_TIME))) - { - goto Exit; - } - - /* Allocate and Initialize FLAIM Shared File System object */ - - if ((gv_FlmSysData.pFileSystem = f_new F_FileSystemImp) == NULL) - { - rc = RC_SET( FERR_MEM); - goto Exit; - } - -#if defined( FLM_WIN) - OSVERSIONINFO versionInfo; - - versionInfo.dwOSVersionInfoSize = sizeof( OSVERSIONINFO); - if( !GetVersionEx( &versionInfo)) - { - return( MapWinErrorToFlaim( GetLastError(), FERR_FAILURE)); - } - - // Async writes are not supported on Win32s (3.1) or - // Win95, 98, ME, etc. - - gv_FlmSysData.bOkToDoAsyncWrites = - (versionInfo.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS && - versionInfo.dwPlatformId != VER_PLATFORM_WIN32s) - ? TRUE - : FALSE; -#else - gv_FlmSysData.bOkToDoAsyncWrites = TRUE; -#endif - - /* Allocate and Initialize FLAIM Server Lock Manager. */ - - if ((gv_FlmSysData.pServerLockMgr = - new ServerLockManager( &gv_FlmSysData.hServerLockMgrMutex)) == NULL) - { - rc = RC_SET( FERR_MEM); - goto Exit; - } - - // Set up the session manager - - if( (gv_FlmSysData.pSessionMgr = f_new F_SessionMgr) == NULL) - { - rc = RC_SET( FERR_MEM); - goto Exit; - } - - if( RC_BAD( rc = gv_FlmSysData.pSessionMgr->setupSessionMgr())) - { - goto Exit; - } - - // Set up hash table for lock manager. - - if (RC_BAD( rc = gv_FlmSysData.pServerLockMgr->SetupHashTbl())) - { - goto Exit; - } - - // Set up mutexes for the event table. - - for (iEventCategory = 0; - iEventCategory < F_MAX_EVENT_CATEGORIES; - iEventCategory++) - { - if (RC_BAD( rc = f_mutexCreate( - &gv_FlmSysData.EventHdrs [iEventCategory].hMutex))) - { - goto Exit; - } - } - - // Start the monitor thread - ALWAYS DO LAST. EVERYTHING MUST BE - // SETUP PROPERLY BEFORE STARTING THIS THREAD. - - if (RC_BAD( rc = f_threadCreate( &gv_FlmSysData.pMonitorThrd, - flmMonitor, "DB Monitor"))) - { - goto Exit; - } - -#ifdef FLM_USE_NICI - iHandle = f_getpid(); - - // Initialize NICI - if (CCS_Init(&iHandle)) - { - // Failure. - rc = RC_SET( FERR_NICI_INIT_FAILED); - goto Exit; - } -#endif - -Exit: - - // If not successful, free up any resources that were allocated. - - if (RC_BAD( rc)) - { - flmCleanup(); - } - flmUnlockSysData(); - return( rc); -} - -/**************************************************************************** -Desc: This routine sets the limits for record cache and block cache - dividing - the total cache between the two caches. It uses the same ratio - currently in force. -****************************************************************************/ -FSTATIC RCODE flmSetCacheLimits( - FLMUINT uiNewTotalCacheSize, - FLMBOOL bPreallocateCache) -{ - RCODE rc = FERR_OK; - FLMUINT uiNewBlockCacheSize; - FLMBOOL bResizeAfterConfig = FALSE; - - if( uiNewTotalCacheSize > FLM_MAX_CACHE_SIZE) - { - uiNewTotalCacheSize = FLM_MAX_CACHE_SIZE; - } - - if( gv_FlmSysData.bDynamicCacheAdjust || !bPreallocateCache) - { -DONT_PREALLOCATE: - - if( uiNewTotalCacheSize < gv_FlmSysData.uiMaxCache) - { - bResizeAfterConfig = TRUE; - } - else - { - if( RC_BAD( rc = gv_FlmSysData.pSlabManager->resize( 0))) - { - goto Exit; - } - } - - gv_FlmSysData.bCachePreallocated = FALSE; - } - else - { - if( RC_BAD( rc = gv_FlmSysData.pSlabManager->resize( - uiNewTotalCacheSize, &uiNewTotalCacheSize))) - { - - // Log a message indicating that we couldn't pre-allocate - // the cache - - flmLogMessage( - FLM_DEBUG_MESSAGE, - FLM_YELLOW, - FLM_BLACK, - "WARNING: Couldn't pre-allocate cache."); - - goto DONT_PREALLOCATE; - } - - gv_FlmSysData.bCachePreallocated = TRUE; - } - - if( gv_FlmSysData.uiBlockCachePercentage == 100) - { - uiNewBlockCacheSize = uiNewTotalCacheSize; - } - else - { - uiNewBlockCacheSize = (FLMUINT)((uiNewTotalCacheSize / 100) * - gv_FlmSysData.uiBlockCachePercentage); - } - - if (RC_OK( rc = ScaConfig( FLM_CACHE_LIMIT, - (void *)uiNewBlockCacheSize, (void *)0))) - { - rc = flmRcaConfig( FLM_CACHE_LIMIT, - (void *)(uiNewTotalCacheSize - uiNewBlockCacheSize), - (void *)0); - } - - if( bResizeAfterConfig) - { - (void)gv_FlmSysData.pSlabManager->resize( 0); - } - - gv_FlmSysData.uiMaxCache = uiNewTotalCacheSize; - -Exit: - - return( rc); -} - -/*API~*********************************************************************** -Desc : Configures how memory will be dynamically regulated. -*END************************************************************************/ -FLMEXP RCODE FLMAPI FlmSetDynamicMemoryLimit( - FLMUINT uiCacheAdjustPercent, - FLMUINT uiCacheAdjustMin, - FLMUINT uiCacheAdjustMax, - FLMUINT uiCacheAdjustMinToLeave - ) -{ -#ifndef FLM_CAN_GET_PHYS_MEM - F_UNREFERENCED_PARM( uiCacheAdjustPercent); - F_UNREFERENCED_PARM( uiCacheAdjustMin); - F_UNREFERENCED_PARM( uiCacheAdjustMax); - F_UNREFERENCED_PARM( uiCacheAdjustMinToLeave); - return( RC_SET( FERR_NOT_IMPLEMENTED)); -#else - RCODE rc = FERR_OK; - FLMUINT uiCacheBytes; - - f_mutexLock( gv_FlmSysData.hShareMutex); - f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); - gv_FlmSysData.bDynamicCacheAdjust = TRUE; - flmAssert( uiCacheAdjustPercent > 0 && - uiCacheAdjustPercent <= 100); - gv_FlmSysData.uiCacheAdjustPercent = uiCacheAdjustPercent; - gv_FlmSysData.uiCacheAdjustMin = uiCacheAdjustMin; - gv_FlmSysData.uiCacheAdjustMax = uiCacheAdjustMax; - gv_FlmSysData.uiCacheAdjustMinToLeave = uiCacheAdjustMinToLeave; - uiCacheBytes = flmGetCacheBytes( gv_FlmSysData.uiCacheAdjustPercent, - gv_FlmSysData.uiCacheAdjustMin, - gv_FlmSysData.uiCacheAdjustMax, - gv_FlmSysData.uiCacheAdjustMinToLeave, TRUE, - gv_FlmSysData.SCacheMgr.Usage.uiTotalBytesAllocated + - gv_FlmSysData.RCacheMgr.pRCacheAlloc->getTotalBytesAllocated()); - rc = flmSetCacheLimits( uiCacheBytes, FALSE); - f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); - f_mutexUnlock( gv_FlmSysData.hShareMutex); - return( rc); -#endif -} - -/*API~*********************************************************************** -Desc : Sets a hard memory limit for cache. -*END************************************************************************/ -FLMEXP RCODE FLMAPI FlmSetHardMemoryLimit( - FLMUINT uiPercent, - FLMBOOL bPercentOfAvail, - FLMUINT uiMin, - FLMUINT uiMax, - FLMUINT uiMinToLeave, - FLMBOOL bPreallocate - ) -{ - RCODE rc = FERR_OK; - - f_mutexLock( gv_FlmSysData.hShareMutex); - f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); - gv_FlmSysData.bDynamicCacheAdjust = FALSE; - if (uiPercent) - { -#ifndef FLM_CAN_GET_PHYS_MEM - F_UNREFERENCED_PARM( bPercentOfAvail); - F_UNREFERENCED_PARM( uiMin); - F_UNREFERENCED_PARM( uiMinToLeave); - rc = RC_SET( FERR_NOT_IMPLEMENTED); -#else - FLMUINT uiCacheBytes; - - uiCacheBytes = flmGetCacheBytes( uiPercent, uiMin, uiMax, uiMinToLeave, - bPercentOfAvail, - gv_FlmSysData.SCacheMgr.Usage.uiTotalBytesAllocated + - gv_FlmSysData.RCacheMgr.pRCacheAlloc->getTotalBytesAllocated()); - rc = flmSetCacheLimits( uiCacheBytes, bPreallocate); -#endif - } - else - { - rc = flmSetCacheLimits( uiMax, bPreallocate); - } - f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); - f_mutexUnlock( gv_FlmSysData.hShareMutex); - return( rc); -} - -/*API~*********************************************************************** -Desc : Returns information about memory usage. -*END************************************************************************/ -FLMEXP void FLMAPI FlmGetMemoryInfo( - FLM_MEM_INFO * pMemInfo) -{ - f_memset( pMemInfo, 0, sizeof( FLM_MEM_INFO)); - f_mutexLock( gv_FlmSysData.hShareMutex); - f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); - pMemInfo->bDynamicCacheAdjust = gv_FlmSysData.bDynamicCacheAdjust; - pMemInfo->uiCacheAdjustPercent = gv_FlmSysData.uiCacheAdjustPercent; - pMemInfo->uiCacheAdjustMin = gv_FlmSysData.uiCacheAdjustMin; - pMemInfo->uiCacheAdjustMax = gv_FlmSysData.uiCacheAdjustMax; - pMemInfo->uiCacheAdjustMinToLeave = gv_FlmSysData.uiCacheAdjustMinToLeave; - - // Return record cache information. - - f_memcpy( &pMemInfo->RecordCache, &gv_FlmSysData.RCacheMgr.Usage, - sizeof( FLM_CACHE_USAGE)); - - // Return block cache information. - - f_memcpy( &pMemInfo->BlockCache, &gv_FlmSysData.SCacheMgr.Usage, - sizeof( FLM_CACHE_USAGE)); - - pMemInfo->uiFreeBytes = gv_FlmSysData.SCacheMgr.uiFreeBytes; - pMemInfo->uiFreeCount = gv_FlmSysData.SCacheMgr.uiFreeCount; - pMemInfo->uiReplaceableCount = gv_FlmSysData.SCacheMgr.uiReplaceableCount; - pMemInfo->uiReplaceableBytes = gv_FlmSysData.SCacheMgr.uiReplaceableBytes; - - if( gv_FlmSysData.pFileHashTbl) - { - FLMUINT uiLoop; - FFILE * pFile; - - for( uiLoop = 0; uiLoop < FILE_HASH_ENTRIES; uiLoop++) - { - if( (pFile = (FFILE *)gv_FlmSysData.pFileHashTbl[ - uiLoop].pFirstInBucket) != NULL) - { - while( pFile) - { - if( pFile->pECacheMgr) - { - pFile->pECacheMgr->getStats( &pMemInfo->ECache, TRUE); - } - - if( pFile->uiDirtyCacheCount) - { - pMemInfo->uiDirtyBytes += - pFile->uiDirtyCacheCount * pFile->FileHdr.uiBlockSize; - pMemInfo->uiDirtyCount += pFile->uiDirtyCacheCount; - } - - if( pFile->uiNewCount) - { - pMemInfo->uiNewBytes += - pFile->uiNewCount * pFile->FileHdr.uiBlockSize; - pMemInfo->uiNewCount += pFile->uiNewCount; - } - - if( pFile->uiLogCacheCount) - { - pMemInfo->uiLogBytes += - pFile->uiLogCacheCount * pFile->FileHdr.uiBlockSize; - pMemInfo->uiLogCount += pFile->uiLogCacheCount; - } - - pFile = pFile->pNext; - } - } - } - } - - f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); - f_mutexUnlock( gv_FlmSysData.hShareMutex); -} - -/**************************************************************************** -Desc : Close a database file - all background threads are closed too. -****************************************************************************/ -FSTATIC RCODE flmCloseDbFile( - const char * pszDbFileName, - const char * pszDataDir) -{ - RCODE rc = FERR_OK; - FLMBOOL bMutexLocked = FALSE; - FFILE * pFile; - - f_mutexLock( gv_FlmSysData.hShareMutex); - bMutexLocked = TRUE; - -Retry: - - // Look up the file using flmFindFile to see if we have the - // file open. May unlock and re-lock the global mutex. - - if (RC_BAD( rc = flmFindFile( pszDbFileName, pszDataDir, &pFile))) - { - goto Exit; - } - - // If we did not find the file, we are OK. - - if (!pFile) - { - goto Exit; - } - - // If the file is in the process of being opened by another - // thread, wait for the open to complete. - - if (pFile->uiFlags & DBF_BEING_OPENED) - { - if (RC_BAD( rc = flmWaitNotifyReq( gv_FlmSysData.hShareMutex, - &pFile->pOpenNotifies, - (void *)0))) - { - // GW Bug #24307. If flmWaitNotifyReq returns a bad RC, assume that - // the other thread will unlock and free the pFile structure. This - // routine should only unlock the pFile if an error occurs at some - // other point. See flmVerifyFileUse. - - // *ppFileRV is set to NULL at Exit. - goto Exit; - } - - goto Retry; - } - - if( pFile->uiUseCount) - { - // Increment the use count temporarily so that the FFILE won't be - // moved to the NU list because of db close calls made by - // releaseFileResources. - - pFile->uiUseCount++; - - // Must unlock the mutex prior to calling releaseFileResources because - // it may call API-level routines (such as FlmDbClose) that do not - // expect the mutex to be locked. - - f_mutexUnlock( gv_FlmSysData.hShareMutex); - bMutexLocked = FALSE; - - // First, all session resources are released that are - // associated with the specified database - - if( gv_FlmSysData.pSessionMgr) - { - gv_FlmSysData.pSessionMgr->releaseFileResources( pFile); - } - - f_mutexLock( gv_FlmSysData.hShareMutex); - bMutexLocked = TRUE; - - // Decrement the temporary use count that was added above. - // By now, the FFILE may no longer be in use. If so, we need to - // link it to the NU list. - - if (!(--pFile->uiUseCount)) - { - // If the "must close" flag is set, it indicates that - // the FFILE is being forced to close. Put the FFILE in - // the NU list, but specify that it should be quickly - // timed-out. We link the file to the NU list even - // though we are going to unlink it below because - // flmLinkFileToNUList closes the RFL file(s). - - flmLinkFileToNUList( pFile, pFile->bMustClose); - } - } - - // If the FFILE is in the NU list, unlink it - - flmUnlinkFileFromNUList( pFile); - - // If we have non-background threads accessing the database, - // we cannot close the file. - - if (pFile->uiUseCount > pFile->uiInternalUseCount) - { - rc = RC_SET( FERR_TRANS_ACTIVE); - goto Exit; - } - - // Close the file. - - flmFreeFile( pFile); - - // Unlock the mutex - - f_mutexUnlock( gv_FlmSysData.hShareMutex); - bMutexLocked = FALSE; - - // Clean up any unused file handles - - (void)gv_FlmSysData.pFileHdlMgr->CheckAgedItems( (FLMUINT)0); - -Exit: - - if (bMutexLocked) - { - f_mutexUnlock( gv_FlmSysData.hShareMutex); - } - return( rc); -} - -/**************************************************************************** -Desc : Register our http callback function -****************************************************************************/ -FSTATIC RCODE flmRegisterHttpCallback( - FLM_MODULE_HANDLE hModule, - const char * pszUrlString) -{ - RCODE rc = FERR_OK; - char * pszTemp; - FLMUINT uiRegFlags; - - if (gv_FlmSysData.HttpConfigParms.bRegistered) - { - rc = RC_SET( FERR_HTTP_REGISTER_FAILURE); - goto Exit; - } - - // Need to save the Url string for later use... - - if( RC_BAD( rc = f_alloc( f_strlen( pszUrlString) + 1, &pszTemp))) - { - goto Exit; - } - - f_strcpy( pszTemp, pszUrlString); - - // Set the flags that tell the server what kind of authentication - // we want: - // HR_STK_BOTH = Allow both http and https - // HR_AUTH_USERSA = Allow any user in the NDS tree or the SAdmin user - // HR_REALM_NDS = Use the NDS realm for authentication - - uiRegFlags = HR_STK_BOTH | HR_AUTH_USERSA | HR_REALM_NDS; - - if (gv_FlmSysData.HttpConfigParms.fnReg) - { - if( gv_FlmSysData.HttpConfigParms.fnReg( hModule, pszUrlString, - (FLMUINT32)uiRegFlags, flmHttpCallback, NULL, NULL) != 0) - { - rc = RC_SET( FERR_HTTP_REGISTER_FAILURE); - goto Exit; - } - } - else - { - flmAssert( 0); - rc = RC_SET( FERR_NO_HTTP_STACK); - goto Exit; - } - - // Save the URL string in gv_FlmSysData - - gv_FlmSysData.HttpConfigParms.pszURLString = pszTemp; - gv_FlmSysData.HttpConfigParms.uiURLStringLen = f_strlen( pszTemp); - pszTemp = NULL; - - gv_FlmSysData.HttpConfigParms.bRegistered = TRUE; - -Exit: - - if (RC_BAD( rc)) - { - if (pszTemp) - { - f_free( &pszTemp); - pszTemp = NULL; - } - } - - return( rc); -} - -/**************************************************************************** -Desc : Deregister our http callback function -****************************************************************************/ -FSTATIC RCODE flmDeregisterHttpCallback() -{ - RCODE rc = FERR_OK; - if (!gv_FlmSysData.HttpConfigParms.bRegistered) - { - rc = RC_SET( FERR_HTTP_DEREG_FAILURE); - goto Exit; - } - - if ( !gv_FlmSysData.HttpConfigParms.fnDereg || - !gv_FlmSysData.HttpConfigParms.pszURLString ) - { - flmAssert( 0); - rc = RC_SET( FERR_NO_HTTP_STACK); - goto Exit; - } - - if ( gv_FlmSysData.HttpConfigParms.fnDereg( - gv_FlmSysData.HttpConfigParms.pszURLString, - flmHttpCallback) != 0) - { - rc = RC_SET( FERR_HTTP_DEREG_FAILURE); - goto Exit; - } - - if( gv_FlmSysData.HttpConfigParms.pszURLString) - { - f_free( &gv_FlmSysData.HttpConfigParms.pszURLString); - gv_FlmSysData.HttpConfigParms.pszURLString = NULL; - } - - // Now, tell the callback function to delete the webpage factory and cleanup - // the allocated memory etc... - f_mutexLock( gv_FlmSysData.HttpConfigParms.hMutex); - while (gv_FlmSysData.HttpConfigParms.uiUseCount) - { - f_mutexUnlock( gv_FlmSysData.HttpConfigParms.hMutex); - f_sleep( 10); - f_mutexLock( gv_FlmSysData.HttpConfigParms.hMutex); - } - flmHttpCallback( NULL, NULL); - f_mutexUnlock( gv_FlmSysData.HttpConfigParms.hMutex); - - - gv_FlmSysData.HttpConfigParms.bRegistered = FALSE; -Exit: - return( rc); -} - - -/*API~*********************************************************************** -Desc: Configures share attributes. -*END************************************************************************/ -FLMEXP RCODE FLMAPI FlmConfig( - eFlmConfigTypes eConfigType, - void * Value1, - void * Value2) -{ - RCODE rc = FERR_OK; - FLMUINT uiValue; - FLMUINT uiSave; - FLMUINT uiCurrTime; - FLMUINT uiSaveMax; - - switch( eConfigType) - { - - case FLM_OPEN_THRESHOLD: - uiValue = (FLMUINT) Value1; - rc = gv_FlmSysData.pFileHdlMgr->SetOpenThreshold( uiValue); - break; - - case FLM_CACHE_LIMIT: - { - f_mutexLock( gv_FlmSysData.hShareMutex); - f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); - gv_FlmSysData.bDynamicCacheAdjust = FALSE; - rc = flmSetCacheLimits( (FLMUINT)Value1, (FLMBOOL)Value2); - f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); - f_mutexUnlock( gv_FlmSysData.hShareMutex); - break; - } - - case FLM_BLOCK_CACHE_PERCENTAGE: - { - f_mutexLock( gv_FlmSysData.hShareMutex); - f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); - - if( (gv_FlmSysData.uiBlockCachePercentage = (FLMUINT)Value1) > 100) - { - gv_FlmSysData.uiBlockCachePercentage = 100; - } - - rc = flmSetCacheLimits( - gv_FlmSysData.SCacheMgr.Usage.uiMaxBytes + - gv_FlmSysData.RCacheMgr.Usage.uiMaxBytes, - gv_FlmSysData.bCachePreallocated); - - f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); - f_mutexUnlock( gv_FlmSysData.hShareMutex); - break; - } - - case FLM_SCACHE_DEBUG: - f_mutexLock( gv_FlmSysData.hShareMutex); - f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); - if (RC_OK( rc = ScaConfig( FLM_SCACHE_DEBUG, Value1, Value2))) - { - rc = flmRcaConfig( FLM_SCACHE_DEBUG, Value1, Value2); - } - f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); - f_mutexUnlock( gv_FlmSysData.hShareMutex); - break; - - case FLM_CLOSE_UNUSED_FILES: - - // Timeout inactive sessions - - if( gv_FlmSysData.pSessionMgr) - { - gv_FlmSysData.pSessionMgr->timeoutInactiveSessions( - (FLMUINT)Value1, TRUE); - } - - // Convert seconds to timer units - - FLM_SECS_TO_TIMER_UNITS( (FLMUINT) Value1, uiValue); - rc = gv_FlmSysData.pFileHdlMgr->CheckAgedItems( uiValue); - - // Free any other unused structures that have not been used for the - // specified amount of time. - - uiCurrTime = (FLMUINT)FLM_GET_TIMER(); - f_mutexLock( gv_FlmSysData.hShareMutex); - - // Temporarily set the maximum unused seconds in the FLMSYSDATA structure - // to the value that was passed in to Value1. Restore it after - // calling flmCheckNUStructs. - - uiSave = gv_FlmSysData.uiMaxUnusedTime; - gv_FlmSysData.uiMaxUnusedTime = uiValue; - - // May unlock and re-lock the global mutex. - flmCheckNUStructs( uiCurrTime); - gv_FlmSysData.uiMaxUnusedTime = uiSave; - f_mutexUnlock( gv_FlmSysData.hShareMutex); - break; - - case FLM_CLOSE_ALL_FILES: - rc = flmCloseAllFiles(); - break; - - case FLM_START_STATS: - (void)flmStatStart( &gv_FlmSysData.Stats); - - // Start query statistics, if they have not - // already been started. - - f_mutexLock( gv_FlmSysData.hQueryMutex); - if (!gv_FlmSysData.uiMaxQueries) - { - gv_FlmSysData.uiMaxQueries = 20; - gv_FlmSysData.bNeedToUnsetMaxQueries = TRUE; - } - f_mutexUnlock( gv_FlmSysData.hQueryMutex); - break; - - case FLM_STOP_STATS: - (void)flmStatStop( &gv_FlmSysData.Stats); - - // Stop query statistics, if they were - // started by a call to FLM_START_STATS. - - f_mutexLock( gv_FlmSysData.hQueryMutex); - if (gv_FlmSysData.bNeedToUnsetMaxQueries) - { - gv_FlmSysData.uiMaxQueries = 0; - flmFreeSavedQueries( TRUE); - // NOTE: flmFreeSavedQueries unlocks the mutex. - } - else - { - f_mutexUnlock( gv_FlmSysData.hQueryMutex); - } - break; - - case FLM_RESET_STATS: - - // Lock the record cache manager's mutex - - f_mutexLock( gv_FlmSysData.hShareMutex); - f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); - - // Reset the record cache statistics - - gv_FlmSysData.RCacheMgr.uiIoWaits = 0; - gv_FlmSysData.RCacheMgr.Usage.uiCacheHits = 0; - gv_FlmSysData.RCacheMgr.Usage.uiCacheHitLooks = 0; - gv_FlmSysData.RCacheMgr.Usage.uiCacheFaults = 0; - gv_FlmSysData.RCacheMgr.Usage.uiCacheFaultLooks = 0; - - // Reset the block cache statistics. - - gv_FlmSysData.SCacheMgr.uiIoWaits = 0; - gv_FlmSysData.SCacheMgr.Usage.uiCacheHits = 0; - gv_FlmSysData.SCacheMgr.Usage.uiCacheHitLooks = 0; - gv_FlmSysData.SCacheMgr.Usage.uiCacheFaults = 0; - gv_FlmSysData.SCacheMgr.Usage.uiCacheFaultLooks = 0; - - // Unlock the cache manager's mutex - - f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); - f_mutexUnlock( gv_FlmSysData.hShareMutex); - - (void)flmStatReset( &gv_FlmSysData.Stats, FALSE, TRUE); - - f_mutexLock( gv_FlmSysData.hQueryMutex); - uiSaveMax = gv_FlmSysData.uiMaxQueries; - gv_FlmSysData.uiMaxQueries = 0; - flmFreeSavedQueries( TRUE); - // NOTE: flmFreeSavedQueries unlocks the mutex. - - // Restore the old maximum - - if (uiSaveMax) - { - - // flmFreeSavedQueries unlocks the mutex, so we - // must relock it to restore the old maximum. - - f_mutexLock( gv_FlmSysData.hQueryMutex); - gv_FlmSysData.uiMaxQueries = uiSaveMax; - f_mutexUnlock( gv_FlmSysData.hQueryMutex); - } - break; - - case FLM_TMPDIR: - f_mutexLock( gv_FlmSysData.hShareMutex); - rc = flmSetTmpDir( (const char *)Value1); - f_mutexUnlock( gv_FlmSysData.hShareMutex); - break; - - case FLM_MAX_CP_INTERVAL: - FLM_SECS_TO_TIMER_UNITS( (FLMUINT) Value1, gv_FlmSysData.uiMaxCPInterval); - break; - - case FLM_BLOB_EXT: - { - const char * pszTmp = (const char *)Value1; - - if (!pszTmp) - { - gv_FlmSysData.ucBlobExt [0] = 0; - } - else - { - int iCnt; - - // Don't save any more than 63 characters. - - for (iCnt = 0; - ((iCnt < 63) && (*pszTmp)); - iCnt++, pszTmp++) - { - gv_FlmSysData.ucBlobExt [iCnt] = *pszTmp; - } - gv_FlmSysData.ucBlobExt [iCnt] = 0; - } - } - break; - - case FLM_MAX_TRANS_SECS: - FLM_SECS_TO_TIMER_UNITS( (FLMUINT) Value1, - gv_FlmSysData.uiMaxTransTime); - break; - - case FLM_MAX_TRANS_INACTIVE_SECS: - FLM_SECS_TO_TIMER_UNITS( (FLMUINT) Value1, - gv_FlmSysData.uiMaxTransInactiveTime); - break; - - case FLM_CACHE_ADJUST_INTERVAL: - FLM_SECS_TO_TIMER_UNITS( (FLMUINT)Value1, - gv_FlmSysData.uiCacheAdjustInterval); - break; - - case FLM_CACHE_CLEANUP_INTERVAL: - FLM_SECS_TO_TIMER_UNITS( (FLMUINT)Value1, - gv_FlmSysData.uiCacheCleanupInterval); - break; - - case FLM_UNUSED_CLEANUP_INTERVAL: - FLM_SECS_TO_TIMER_UNITS( (FLMUINT)Value1, - gv_FlmSysData.uiUnusedCleanupInterval); - break; - - case FLM_MAX_UNUSED_TIME: - f_mutexLock( gv_FlmSysData.hShareMutex); - FLM_SECS_TO_TIMER_UNITS( (FLMUINT)Value1, - gv_FlmSysData.uiMaxUnusedTime); - f_mutexUnlock( gv_FlmSysData.hShareMutex); - break; - - case FLM_OUT_OF_MEM_SIMULATION: -#ifdef DEBUG_SIM_OUT_OF_MEM - gv_FlmSysData.uiOutOfMemSimEnabledFlag = - (FLMUINT)((Value1) ? OUT_OF_MEM_SIM_ENABLED_FLAG : 0); -#else - rc = RC_SET( FERR_NOT_IMPLEMENTED); -#endif - break; - - case FLM_CACHE_CHECK: - gv_FlmSysData.bCheckCache = (FLMBOOL)((Value1 != 0) - ? (FLMBOOL)TRUE - : (FLMBOOL)FALSE); - break; - - case FLM_CLOSE_FILE: - rc = flmCloseDbFile( (const char *)Value1, (const char *)Value2); - break; - - case FLM_LOGGER: - f_mutexLock( gv_FlmSysData.hShareMutex); - if( !gv_FlmSysData.pLogger && Value1) - { - gv_FlmSysData.pLogger = (F_Logger *)Value1; - gv_FlmSysData.pLogger->lockLogger(); - gv_FlmSysData.pLogger->AddRef(); - gv_FlmSysData.pLogger->unlockLogger(); - } - f_mutexUnlock( gv_FlmSysData.hShareMutex); - break; - - case FLM_USE_ESM: - gv_FlmSysData.bOkToUseESM = (FLMBOOL)((Value1 != 0) - ? (FLMBOOL)TRUE - : (FLMBOOL)FALSE); - break; - - case FLM_ASSIGN_HTTP_SYMS: - //Note: before you attempt this, you had better have loaded the - //necessary shared library... - if ( gv_FlmSysData.HttpConfigParms.fnReg || - gv_FlmSysData.HttpConfigParms.fnDereg || - gv_FlmSysData.HttpConfigParms.fnReqPath || - gv_FlmSysData.HttpConfigParms.fnReqQuery || - gv_FlmSysData.HttpConfigParms.fnReqHdrValue || - gv_FlmSysData.HttpConfigParms.fnSetHdrValue || - gv_FlmSysData.HttpConfigParms.fnPrintf || - gv_FlmSysData.HttpConfigParms.fnEmit || - gv_FlmSysData.HttpConfigParms.fnSetNoCache || - gv_FlmSysData.HttpConfigParms.fnSendHeader || - gv_FlmSysData.HttpConfigParms.fnSetIOMode || - gv_FlmSysData.HttpConfigParms.fnSendBuffer || - gv_FlmSysData.HttpConfigParms.fnAcquireSession || - gv_FlmSysData.HttpConfigParms.fnReleaseSession || - gv_FlmSysData.HttpConfigParms.fnAcquireUser || - gv_FlmSysData.HttpConfigParms.fnReleaseUser || - gv_FlmSysData.HttpConfigParms.fnSetSessionValue || - gv_FlmSysData.HttpConfigParms.fnGetSessionValue || - gv_FlmSysData.HttpConfigParms.fnGetGblValue || - gv_FlmSysData.HttpConfigParms.fnSetGblValue || - gv_FlmSysData.HttpConfigParms.fnRecvBuffer ) - { - rc = RC_SET( FERR_HTTP_SYMS_EXIST); - goto Exit; - } - else - { - gv_FlmSysData.HttpConfigParms.fnReg = ((HTTPCONFIGPARAMS *)Value1)->fnReg; - gv_FlmSysData.HttpConfigParms.fnDereg = ((HTTPCONFIGPARAMS *)Value1)->fnDereg; - gv_FlmSysData.HttpConfigParms.fnReqPath = ((HTTPCONFIGPARAMS *)Value1)->fnReqPath; - gv_FlmSysData.HttpConfigParms.fnReqQuery = ((HTTPCONFIGPARAMS *)Value1)->fnReqQuery; - gv_FlmSysData.HttpConfigParms.fnReqHdrValue = ((HTTPCONFIGPARAMS *)Value1)->fnReqHdrValue; - gv_FlmSysData.HttpConfigParms.fnSetHdrValue = ((HTTPCONFIGPARAMS *)Value1)->fnSetHdrValue; - gv_FlmSysData.HttpConfigParms.fnPrintf = ((HTTPCONFIGPARAMS *)Value1)->fnPrintf; - gv_FlmSysData.HttpConfigParms.fnEmit = ((HTTPCONFIGPARAMS *)Value1)->fnEmit; - gv_FlmSysData.HttpConfigParms.fnSetNoCache = ((HTTPCONFIGPARAMS *)Value1)->fnSetNoCache; - gv_FlmSysData.HttpConfigParms.fnSendHeader = ((HTTPCONFIGPARAMS *)Value1)->fnSendHeader; - gv_FlmSysData.HttpConfigParms.fnSetIOMode = ((HTTPCONFIGPARAMS *)Value1)->fnSetIOMode; - gv_FlmSysData.HttpConfigParms.fnSendBuffer = ((HTTPCONFIGPARAMS *)Value1)->fnSendBuffer; - gv_FlmSysData.HttpConfigParms.fnAcquireSession = ((HTTPCONFIGPARAMS *)Value1)->fnAcquireSession; - gv_FlmSysData.HttpConfigParms.fnReleaseSession = ((HTTPCONFIGPARAMS *)Value1)->fnReleaseSession; - gv_FlmSysData.HttpConfigParms.fnAcquireUser = ((HTTPCONFIGPARAMS *)Value1)->fnAcquireUser; - gv_FlmSysData.HttpConfigParms.fnReleaseUser = ((HTTPCONFIGPARAMS *)Value1)->fnReleaseUser; - gv_FlmSysData.HttpConfigParms.fnSetSessionValue = ((HTTPCONFIGPARAMS *)Value1)->fnSetSessionValue; - gv_FlmSysData.HttpConfigParms.fnGetSessionValue = ((HTTPCONFIGPARAMS *)Value1)->fnGetSessionValue; - gv_FlmSysData.HttpConfigParms.fnGetGblValue = ((HTTPCONFIGPARAMS *)Value1)->fnGetGblValue; - gv_FlmSysData.HttpConfigParms.fnSetGblValue = ((HTTPCONFIGPARAMS *)Value1)->fnSetGblValue; - gv_FlmSysData.HttpConfigParms.fnRecvBuffer = ((HTTPCONFIGPARAMS *)Value1)->fnRecvBuffer; - } - break; - - case FLM_REGISTER_HTTP_URL: - // Value1: FLM_MODULE_HANDLE - // Value2: Url string - if ((Value1 == NULL) || (Value2 == NULL)) - { - rc = RC_SET( FERR_INVALID_PARM); - goto Exit; - } - - rc = flmRegisterHttpCallback((FLM_MODULE_HANDLE)Value1, (char *)Value2); - break; - - case FLM_DEREGISTER_HTTP_URL: - rc = flmDeregisterHttpCallback(); - break; - - case FLM_UNASSIGN_HTTP_SYMS: - gv_FlmSysData.HttpConfigParms.fnReg = NULL; - gv_FlmSysData.HttpConfigParms.fnDereg = NULL; - gv_FlmSysData.HttpConfigParms.fnReqPath = NULL; - gv_FlmSysData.HttpConfigParms.fnReqQuery = NULL; - gv_FlmSysData.HttpConfigParms.fnReqHdrValue = NULL; - gv_FlmSysData.HttpConfigParms.fnSetHdrValue = NULL; - gv_FlmSysData.HttpConfigParms.fnPrintf = NULL; - gv_FlmSysData.HttpConfigParms.fnEmit = NULL; - gv_FlmSysData.HttpConfigParms.fnSetNoCache = NULL; - gv_FlmSysData.HttpConfigParms.fnSendHeader = NULL; - gv_FlmSysData.HttpConfigParms.fnSetIOMode = NULL; - gv_FlmSysData.HttpConfigParms.fnSendBuffer = NULL; - gv_FlmSysData.HttpConfigParms.fnAcquireSession = NULL; - gv_FlmSysData.HttpConfigParms.fnReleaseSession = NULL; - gv_FlmSysData.HttpConfigParms.fnAcquireUser = NULL; - gv_FlmSysData.HttpConfigParms.fnReleaseUser = NULL; - gv_FlmSysData.HttpConfigParms.fnSetSessionValue = NULL; - gv_FlmSysData.HttpConfigParms.fnGetSessionValue = NULL; - gv_FlmSysData.HttpConfigParms.fnGetGblValue = NULL; - gv_FlmSysData.HttpConfigParms.fnSetGblValue = NULL; - gv_FlmSysData.HttpConfigParms.fnRecvBuffer = NULL; - - break; - - case FLM_KILL_DB_HANDLES: - { - FFILE * pTmpFile; - - f_mutexLock( gv_FlmSysData.hShareMutex); - if( Value1) - { - // Look up the file using flmFindFile to see if we have the - // file open. May unlock and re-lock the global mutex. - - if( RC_OK( flmFindFile( (const char *)Value1, - (const char *)Value2, &pTmpFile)) && pTmpFile) - { - flmSetMustCloseFlags( pTmpFile, FERR_OK, TRUE); - } - } - else - { - if( gv_FlmSysData.pFileHashTbl) - { - FLMUINT uiLoop; - - for( uiLoop = 0; uiLoop < FILE_HASH_ENTRIES; uiLoop++) - { - pTmpFile = - (FFILE *)gv_FlmSysData.pFileHashTbl[ uiLoop].pFirstInBucket; - - while( pTmpFile) - { - flmSetMustCloseFlags( pTmpFile, FERR_OK, TRUE); - pTmpFile = pTmpFile->pNext; - } - } - } - } - - f_mutexUnlock( gv_FlmSysData.hShareMutex); - - // Kill all sessions - - if( gv_FlmSysData.pSessionMgr) - { - gv_FlmSysData.pSessionMgr->shutdownSessions(); - } - - break; - } - - case FLM_QUERY_MAX: - f_mutexLock( gv_FlmSysData.hQueryMutex); - gv_FlmSysData.uiMaxQueries = (FLMUINT)Value1; - gv_FlmSysData.bNeedToUnsetMaxQueries = FALSE; - flmFreeSavedQueries( TRUE); - - // flmFreeSavedQueries unlocks the mutex. - - break; - - case FLM_MAX_DIRTY_CACHE: - f_mutexLock( gv_FlmSysData.hShareMutex); - if (!Value1) - { - gv_FlmSysData.SCacheMgr.bAutoCalcMaxDirty = TRUE; - gv_FlmSysData.SCacheMgr.uiMaxDirtyCache = 0; - gv_FlmSysData.SCacheMgr.uiLowDirtyCache = 0; - } - else - { - gv_FlmSysData.SCacheMgr.bAutoCalcMaxDirty = FALSE; - gv_FlmSysData.SCacheMgr.uiMaxDirtyCache = (FLMUINT)Value1; - - // Low threshhold must be no higher than maximum! - - if ((gv_FlmSysData.SCacheMgr.uiLowDirtyCache = - (FLMUINT)Value2) > gv_FlmSysData.SCacheMgr.uiMaxDirtyCache) - { - gv_FlmSysData.SCacheMgr.uiLowDirtyCache = - gv_FlmSysData.SCacheMgr.uiMaxDirtyCache; - } - } - f_mutexUnlock( gv_FlmSysData.hShareMutex); - break; - - case FLM_QUERY_STRATIFY_LIMITS: - f_mutexLock( gv_FlmSysData.hShareMutex); - gv_FlmSysData.uiMaxStratifyIterations = (FLMUINT)Value1; - gv_FlmSysData.uiMaxStratifyTime = (FLMUINT)Value2; - f_mutexUnlock( gv_FlmSysData.hShareMutex); - break; - - default: - rc = RC_SET( FERR_NOT_IMPLEMENTED); - break; - } - -Exit: - return( rc); -} - -/*API~*********************************************************************** -Desc : Gets configured shared attributes. -*END************************************************************************/ -FLMEXP RCODE FLMAPI FlmGetConfig( - eFlmConfigTypes eConfigType, - void * Value1 - ) -{ - RCODE rc = FERR_OK; - FLMUINT uiTmp; - - switch( eConfigType) - { - case FLM_CACHE_LIMIT: - f_mutexLock( gv_FlmSysData.hShareMutex); - f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); - *((FLMUINT *)Value1) = gv_FlmSysData.SCacheMgr.Usage.uiMaxBytes + - gv_FlmSysData.RCacheMgr.Usage.uiMaxBytes; - f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); - f_mutexUnlock( gv_FlmSysData.hShareMutex); - break; - - case FLM_BLOCK_CACHE_PERCENTAGE: - f_mutexLock( gv_FlmSysData.hShareMutex); - *((FLMUINT *)Value1) = gv_FlmSysData.uiBlockCachePercentage; - f_mutexUnlock( gv_FlmSysData.hShareMutex); - break; - - case FLM_SCACHE_DEBUG: -#ifdef FLM_DEBUG - *((FLMBOOL *)Value1) = gv_FlmSysData.SCacheMgr.bDebug; -#else - *((FLMBOOL *)Value1) = FALSE; -#endif - break; - - case FLM_OPEN_FILES: - *((FLMUINT *)Value1) = gv_FlmSysData.pFileHdlMgr->GetOpenedFiles(); - break; - - case FLM_OPEN_THRESHOLD: - *((FLMUINT *)Value1) = gv_FlmSysData.pFileHdlMgr->GetOpenThreshold(); - break; - - case FLM_TMPDIR: - f_mutexLock( gv_FlmSysData.hShareMutex); - - if( !gv_FlmSysData.bTempDirSet ) - { - rc = RC_SET( FERR_IO_PATH_NOT_FOUND ); - - // Set the output to nulls on failure. - - *((char *)Value1) = 0; - } - else - { - f_strcpy( (char *)Value1, gv_FlmSysData.szTempDir); - } - - f_mutexUnlock( gv_FlmSysData.hShareMutex); - break; - - case FLM_MAX_CP_INTERVAL: - FLM_TIMER_UNITS_TO_SECS( gv_FlmSysData.uiMaxCPInterval, uiTmp); - *((FLMUINT *)Value1) = uiTmp; - break; - - case FLM_BLOB_EXT: - f_strcpy( (char *)Value1, (const char *)gv_FlmSysData.ucBlobExt); - break; - - case FLM_MAX_TRANS_SECS: - FLM_TIMER_UNITS_TO_SECS( gv_FlmSysData.uiMaxTransTime, uiTmp); - *((FLMUINT *)Value1) = uiTmp; - break; - - case FLM_MAX_TRANS_INACTIVE_SECS: - FLM_TIMER_UNITS_TO_SECS( gv_FlmSysData.uiMaxTransInactiveTime, uiTmp); - *((FLMUINT *)Value1) = (FLMUINT)uiTmp; - break; - - case FLM_CACHE_ADJUST_INTERVAL: - FLM_TIMER_UNITS_TO_SECS( gv_FlmSysData.uiCacheAdjustInterval, uiTmp); - *((FLMUINT *)Value1) = uiTmp; - break; - - case FLM_CACHE_CLEANUP_INTERVAL: - FLM_TIMER_UNITS_TO_SECS( gv_FlmSysData.uiCacheCleanupInterval, uiTmp); - *((FLMUINT *)Value1) = uiTmp; - break; - - case FLM_UNUSED_CLEANUP_INTERVAL: - FLM_TIMER_UNITS_TO_SECS( gv_FlmSysData.uiUnusedCleanupInterval, uiTmp); - *((FLMUINT *)Value1) = uiTmp; - break; - - case FLM_MAX_UNUSED_TIME: - f_mutexLock( gv_FlmSysData.hShareMutex); - FLM_TIMER_UNITS_TO_SECS( gv_FlmSysData.uiMaxUnusedTime, uiTmp); - f_mutexUnlock( gv_FlmSysData.hShareMutex); - *((FLMUINT *)Value1) = uiTmp; - break; - - case FLM_OUT_OF_MEM_SIMULATION: -#ifdef DEBUG_SIM_OUT_OF_MEM - *((FLMBOOL *)Value1) = - (gv_FlmSysData.uiOutOfMemSimEnabledFlag == - OUT_OF_MEM_SIM_ENABLED_FLAG) - ? TRUE - : FALSE; -#else - *((FLMBOOL *)Value1) = FALSE; -#endif - break; - - case FLM_CACHE_CHECK: - *((FLMBOOL *)Value1) = gv_FlmSysData.bCheckCache; - break; - - case FLM_USE_ESM: - *((FLMBOOL *)Value1) = gv_FlmSysData.bOkToUseESM; - break; - - case FLM_QUERY_MAX: - f_mutexLock( gv_FlmSysData.hQueryMutex); - *((FLMUINT *)Value1) = gv_FlmSysData.uiMaxQueries; - f_mutexUnlock( gv_FlmSysData.hQueryMutex); - break; - - case FLM_MAX_DIRTY_CACHE: - f_mutexLock( gv_FlmSysData.hShareMutex); - *((FLMUINT *)Value1) = gv_FlmSysData.SCacheMgr.uiMaxDirtyCache; - f_mutexUnlock( gv_FlmSysData.hShareMutex); - break; - - case FLM_DYNA_CACHE_SUPPORTED: -#ifdef FLM_CAN_GET_PHYS_MEM - *((FLMBOOL *)Value1) = TRUE; -#else - *((FLMBOOL *)Value1) = FALSE; -#endif - break; - - case FLM_QUERY_STRATIFY_LIMITS: - f_mutexLock( gv_FlmSysData.hShareMutex); - if (Value1) - { - *((FLMUINT *)Value1) = gv_FlmSysData.uiMaxStratifyIterations; - } - f_mutexUnlock( gv_FlmSysData.hShareMutex); - break; - - default: - rc = RC_SET( FERR_NOT_IMPLEMENTED); - break; - } - -//Exit: - return( rc); -} - -/*API~*********************************************************************** -Desc: -*END************************************************************************/ -FLMEXP RCODE FLMAPI FlmGetThreadInfo( - POOL * pPool, - F_THREAD_INFO ** ppThreadInfo, - FLMUINT * puiNumThreads, - const char * pszUrl) -{ - RCODE rc = FERR_OK; - CS_CONTEXT_p pCSContext = NULL; - - if( pszUrl) - { - // flmGetCSConnection may return a NULL CS_CONTEXT if the - // URL references a local resource. - - if( RC_BAD( rc = flmGetCSConnection( pszUrl, &pCSContext))) - { - goto Exit; - } - } - - if( pCSContext) - { - NODE * pTree; - FCL_WIRE Wire; - - Wire.setContext( pCSContext); - - // Send a request get statistics - - if (RC_BAD( Wire.sendOp( FCS_OPCLASS_GLOBAL, - FCS_OP_GLOBAL_GET_THREAD_INFO))) - { - goto Exit; - } - - if (RC_BAD( Wire.sendTerminate())) - { - goto Exit; - } - - // Read the response. - - if (RC_BAD( Wire.read())) - { - goto Exit; - } - - if (RC_BAD( Wire.getRCode())) - { - goto Exit; - } - - if( RC_BAD( Wire.getHTD( pPool, &pTree))) - { - goto Exit; - } - - if( RC_BAD( rc = fcsExtractThreadInfo( pTree, pPool, - ppThreadInfo, puiNumThreads))) - { - goto Exit; - } - } - else - { - if( RC_BAD( rc = gv_FlmSysData.pThreadMgr->getThreadInfo( pPool, - ppThreadInfo, puiNumThreads))) - { - goto Exit; - } - } - -Exit: - - if( pCSContext) - { - flmCloseCSConnection( &pCSContext); - } - - return( rc); -} - -/**************************************************************************** -Desc: Returns the temporary directory (path) that is part of the share - structure. We have to pass in the FDB structure in order - to know if we should lock a mutex in order to access the - path structure. -****************************************************************************/ -RCODE flmGetTmpDir( - char * pszOutputTmpDir) -{ - RCODE rc = FERR_OK; - - f_mutexLock( gv_FlmSysData.hShareMutex); - - // Return an error if the temp directory has not been set. - - if( !gv_FlmSysData.bTempDirSet) - { - rc = RC_SET( FERR_IO_PATH_NOT_FOUND ); - - // Set the output to nulls on failure. - *pszOutputTmpDir = 0; - } - else - { - f_strcpy( pszOutputTmpDir, gv_FlmSysData.szTempDir); - } - - f_mutexUnlock( gv_FlmSysData.hShareMutex); - return( rc); -} - -/**************************************************************************** -Desc: This shuts down the background threads -Note: This routine assumes that the global mutex is locked. The mutex will - be unlocked internally, but will always be locked on exit. -****************************************************************************/ -FSTATIC void flmShutdownDbThreads( - FFILE * pFile) -{ - RCODE rc = FERR_OK; - F_BKGND_IX * pBackgroundIx; - FDB * pDb; - F_Thread * pThread; - FLMUINT uiThreadId; - FLMUINT uiThreadCount; - FLMBOOL bMutexLocked = TRUE; - - // Shut down the tracker thread - - if( pFile->pMaintThrd) - { - pFile->pMaintThrd->setShutdownFlag(); - f_semSignal( pFile->hMaintSem); - - // Unlock the global mutex - - f_mutexUnlock( gv_FlmSysData.hShareMutex); - bMutexLocked = FALSE; - - pFile->pMaintThrd->stopThread(); - - // Re-lock the mutex - - f_mutexLock( gv_FlmSysData.hShareMutex); - bMutexLocked = TRUE; - - pFile->pMaintThrd->Release(); - pFile->pMaintThrd = NULL; - - f_semDestroy( &pFile->hMaintSem); - } - - // Signal all background threads to shut down that are - // associated with this FFILE - - for( ;;) - { - uiThreadCount = 0; - - // Shut down all background indexing threads. - - uiThreadId = 0; - for( ;;) - { - if( RC_BAD( rc = gv_FlmSysData.pThreadMgr->getNextGroupThread( - &pThread, FLM_BACKGROUND_INDEXING_THREAD_GROUP, &uiThreadId))) - { - if( rc == FERR_NOT_FOUND) - { - rc = FERR_OK; - break; - } - else - { - flmAssert( 0); - } - } - else - { - pBackgroundIx = (F_BKGND_IX *)pThread->getParm1(); - if( pBackgroundIx && pBackgroundIx->pFile == pFile) - { - // Set the thread's terminate flag. - - uiThreadCount++; - pThread->setShutdownFlag(); - } - - pThread->Release(); - pThread = NULL; - } - } - - // Shut down all threads in the FLM_DB_THREAD_GROUP. - - uiThreadId = 0; - for( ;;) - { - if( RC_BAD( rc = gv_FlmSysData.pThreadMgr->getNextGroupThread( - &pThread, FLM_DB_THREAD_GROUP, &uiThreadId))) - { - if( rc == FERR_NOT_FOUND) - { - rc = FERR_OK; - break; - } - else - { - flmAssert( 0); - } - } - else - { - pDb = (FDB *)pThread->getParm2(); - if (pDb && pDb->pFile == pFile) - { - - // Set the thread's terminate flag. - - uiThreadCount++; - pThread->setShutdownFlag(); - } - - pThread->Release(); - pThread = NULL; - } - } - - if( !uiThreadCount) - { - break; - } - - // Unlock the global mutex - - f_mutexUnlock( gv_FlmSysData.hShareMutex); - bMutexLocked = FALSE; - - // Give the threads a chance to terminate - - f_sleep( 50); - - // Re-lock the mutex and see if any threads are still active - - f_mutexLock( gv_FlmSysData.hShareMutex); - bMutexLocked = TRUE; - } - - // Re-lock the mutex - - if( !bMutexLocked) - { - f_mutexLock( gv_FlmSysData.hShareMutex); - } -} - -/**************************************************************************** -Desc: This routine frees all of the structures associated with a file. - Whoever called this routine has already determined that it is safe - to do so. -Notes: The global mutex is assumed to be locked when entering the - routine. It may be unlocked and re-locked before the routine - exits, however. -****************************************************************************/ -void flmFreeFile( - FFILE * pFile) // File to be freed. -{ - FNOTIFY * pCloseNotifies; - - // See if another thread is in the process of closing - // this FFILE. It is possible for this to happen, since - // the monitor thread may have selected this FFILE to be - // closed because it has been unused for a period of time. - // At the same time, a foreground thread could have called - // FlmConfig to close all unused FFILEs. Since flmFreeFile - // may unlock and re-lock the mutex, there is a small window - // of opportunity for both threads to try to free the same - // FFILE. - - if( pFile->uiFlags & DBF_BEING_CLOSED) - { - return; - } - - // Set the DBF_BEING_CLOSED flag - - pFile->uiFlags |= DBF_BEING_CLOSED; - - // Shut down all background threads before shutting down the CP thread. - - flmShutdownDbThreads( pFile); - - // At this point, the use count better be zero - - flmAssert( pFile->uiUseCount == 0); - - // Unlock the mutex - - f_mutexUnlock( gv_FlmSysData.hShareMutex); - - // Shutdown the checkpoint thread - - if( pFile->pCPThrd) - { - pFile->pCPThrd->stopThread(); - pFile->pCPThrd->Release(); - pFile->pCPThrd = NULL; - } - - f_mutexLock( gv_FlmSysData.hShareMutex); - - // Unlink all of the DICTs that are connected to the file. - - while( pFile->pDictList) - { - flmUnlinkDict( pFile->pDictList); - } - - // Take the file out of its name hash bucket, if any. - - if (pFile->uiBucket != 0xFFFF) - { - flmUnlinkFileFromBucket( pFile); - } - - // Unlink the file from the not-used list - - flmUnlinkFileFromNUList( pFile); - - // Free the RFL data, if any. - - if (pFile->pRfl) - { - pFile->pRfl->Release(); - pFile->pRfl = NULL; - } - - // Free any open notify requests associated with the file. - // We shouldn't have any open notifies at this point, but - // we'll be nice and clean up any resources anyway. - - flmAssert( pFile->pOpenNotifies == NULL); - flmFreeNotifyList( &pFile->pOpenNotifies); - - // Save pCloseNotifies -- we will notify any waiters once the - // FFILE has been freed. - - pCloseNotifies = pFile->pCloseNotifies; - - // Free any dictionary usage structures associated with the file. - - flmFreeDictList( &pFile->pDictList); - - // Free any shared cache associated with the file. - - ScaFreeFileCache( pFile); - - // Free any record cache associated with the file. - - flmRcaFreeFileRecs( pFile); - - // Free the file ID list. This will also remove all file handles - // associated with this file. - - if( pFile->pFileIdList) - { - FLMUINT uiRefCnt; - - uiRefCnt = pFile->pFileIdList->Release(); - flmAssert( !uiRefCnt); - pFile->pFileIdList = NULL; - } - - // Release the lock objects. - - if( pFile->pWriteLockObj) - { - pFile->pWriteLockObj->Release(); - pFile->pWriteLockObj = NULL; - } - - if( pFile->pFileLockObj) - { - pFile->pFileLockObj->Release(); - pFile->pFileLockObj = NULL; - } - - // Close and delete the lock file. - - if( pFile->pLockFileHdl) - { - (void)pFile->pLockFileHdl->Close(); - pFile->pLockFileHdl->Release(); - pFile->pLockFileHdl = NULL; - } - - // Free the write buffer managers. - - if( pFile->pBufferMgr) - { - pFile->pBufferMgr->Release(); - pFile->pBufferMgr = NULL; - } - - // Free the log header write buffer - - if( pFile->pucLogHdrWriteBuf) - { -#ifdef FLM_WIN - (void)VirtualFree( pFile->pucLogHdrWriteBuf, 0, MEM_RELEASE); - pFile->pucLogHdrWriteBuf = NULL; -#elif defined( FLM_LINUX) || defined( FLM_SOLARIS) - free( pFile->pucLogHdrWriteBuf); - pFile->pucLogHdrWriteBuf = NULL; -#else - f_free( &pFile->pucLogHdrWriteBuf); -#endif - } - - GedPoolFree( &pFile->krefPool); - - if( pFile->ppBlocksDone) - { - f_free( &pFile->ppBlocksDone); - pFile->uiBlocksDoneArraySize = 0; - } - - if( pFile->pECacheMgr) - { - pFile->pECacheMgr->Release(); - pFile->pECacheMgr = NULL; - } - - // Free the maintenance thread's semaphore - - if( pFile->hMaintSem != F_SEM_NULL) - { - f_semDestroy( &pFile->hMaintSem); - } - - // Free the database wrapping key - - if( pFile->pDbWrappingKey) - { - pFile->pDbWrappingKey->Release(); - pFile->pDbWrappingKey = NULL; - } - - // Free the password - - if ( pFile->pszDbPassword) - { - f_free( &pFile->pszDbPassword); - } - - // Free the FFILE - - f_free( &pFile); - - // Notify waiters that the FFILE is gone - - while( pCloseNotifies) - { - F_SEM hSem; - - *(pCloseNotifies->pRc) = FERR_OK; - hSem = pCloseNotifies->hSem; - pCloseNotifies = pCloseNotifies->pNext; - f_semSignal( hSem); - } - - // Global mutex is still locked at this point -} - -/**************************************************************************** -Desc: This routine frees a registered event. -****************************************************************************/ -FSTATIC void flmFreeEvent( - FEVENT * pEvent, - F_MUTEX hMutex, - FEVENT ** ppEventListRV - ) -{ - f_mutexLock( hMutex); - if (pEvent->pPrev) - { - pEvent->pPrev->pNext = pEvent->pNext; - } - else - { - *ppEventListRV = pEvent->pNext; - } - if (pEvent->pNext) - { - pEvent->pNext->pPrev = pEvent->pPrev; - } - f_mutexUnlock( hMutex); - f_free( &pEvent); -} - -/************************************************************************ -Desc : Cleans up - assumes that the spin lock has already been - obtained. This allows it to be called directly from - FlmStartup on error conditions. -************************************************************************/ -FSTATIC void flmCleanup( void) -{ - FLMUINT uiCnt; - FLMINT iEventCategory; - - // NOTE: We are checking and decrementing a global variable here. - // However, on platforms that properly support atomic exchange, - // we are OK, because the caller has obtained a spin lock before - // calling this routine, so we are guaranteed to be the only thread - // executing this code at this point. On platforms that don't - // support atomic exchange, our spin lock will be less reliable for - // really tight race conditions. But in reality, nobody should be - // calling FlmStartup and FlmShutdown in race conditions like that - // anyway. We are only doing the spin lock stuff to try and be - // nice about it if they are. - - // This check allows FlmShutdown to be called before calling - // FlmStartup, or even if FlmStartup fails. - - if (!gv_uiFlmSysStartupCount) - { - return; - } - - // If we decrement the count and it doesn't go to zero, we are not - // ready to do cleanup yet. - - if (--gv_uiFlmSysStartupCount > 0) - { - return; - } - - // Deregister the Http Callback function if it has been registered. - // Note: Usually, this is all handled at the SMI level (in - // SMDIBHandle::exit). This code is here for the cases where we're - // part of Flint or some other utility that doesn't necessarily use - // SMI... - if (gv_FlmSysData.HttpConfigParms.fnDereg) - { - FlmConfig( FLM_DEREGISTER_HTTP_URL, NULL, NULL); - } - - // Free any queries that have been saved in the query list. - - if (gv_FlmSysData.hQueryMutex != F_MUTEX_NULL) - { - - // Setting uiMaxQueries to zero will cause flmFreeSavedQueries - // to free the entire list. Also, embedded queries will not be - // added back into the list when uiMaxQueries is zero. - - gv_FlmSysData.uiMaxQueries = 0; - flmFreeSavedQueries( FALSE); - } - - // Shut down the monitor thread, if there is one. - - f_threadDestroy( &gv_FlmSysData.pMonitorThrd); - - // Shut down the session manager - - if( gv_FlmSysData.pSessionMgr) - { - gv_FlmSysData.pSessionMgr->Release(); - gv_FlmSysData.pSessionMgr = NULL; - } - - // Destroy the session mutex - - if( gv_FlmSysData.hHttpSessionMutex != F_MUTEX_NULL) - { - f_mutexDestroy( &gv_FlmSysData.hHttpSessionMutex); - } - - gv_FlmSysData.pMrnuFile = NULL; - gv_FlmSysData.pLrnuFile = NULL; - - /* Free all of the files and associated structures. */ - - if (gv_FlmSysData.pFileHashTbl) - { - FBUCKET * pFileHashTbl; - - // flmFreeFile expects the global mutex to be locked - // IMPORTANT NOTE: pFileHashTbl is ALWAYS allocated - // AFTER the mutex is allocated, so we are guaranteed - // to have a mutex if pFileHashTbl is non-NULL. - - f_mutexLock( gv_FlmSysData.hShareMutex); - for (uiCnt = 0, pFileHashTbl = gv_FlmSysData.pFileHashTbl; - uiCnt < FILE_HASH_ENTRIES; - uiCnt++, pFileHashTbl++) - { - FFILE * pFile = (FFILE *)pFileHashTbl->pFirstInBucket; - FFILE * pTmpFile; - - while( pFile) - { - pTmpFile = pFile; - pFile = pFile->pNext; - flmFreeFile( pTmpFile); - } - pFileHashTbl->pFirstInBucket = NULL; - } - - // Unlock the global mutex - f_mutexUnlock( gv_FlmSysData.hShareMutex); - - // Free the hash table - f_free( &gv_FlmSysData.pFileHashTbl); - } - - // Free the statistics. - - if (gv_FlmSysData.bStatsInitialized) - { - FlmFreeStats( &gv_FlmSysData.Stats); - gv_FlmSysData.bStatsInitialized = FALSE; - } - - // Free (release) FLAIM's File Shared File Handles. - - if (gv_FlmSysData.pFileHdlMgr) - { - FLMUINT uiRefCnt = gv_FlmSysData.pFileHdlMgr->Release(); - - // No one else should have a reference to the file handle manager - // after this point. - -#ifdef FLM_DEBUG - flmAssert( 0 == uiRefCnt); -#else - // Quiet the compiler about the unused variable - (void)uiRefCnt; -#endif - gv_FlmSysData.pFileHdlMgr = NULL; - } - - // Free (release) FLAIM's Server Lock Manager. - - if (gv_FlmSysData.pServerLockMgr) - { - FLMUINT uiRefCnt; - - // Release all locks. - - gv_FlmSysData.pServerLockMgr->CheckLockTimeouts( TRUE); - - // Release the lock manager. - - uiRefCnt = gv_FlmSysData.pServerLockMgr->Release(); - - // No one else should have a reference to the server lock manager - // at this point, so lets trip a flmAssert if the object was really - // not deleted. - -#ifdef FLM_DEBUG - flmAssert( 0 == uiRefCnt); -#else - // Quiet the compiler about the unused variable - (void)uiRefCnt; -#endif - gv_FlmSysData.pServerLockMgr = NULL; - } - - // Free the resources of the shared cache manager. - - ScaExit(); - - // Free the resources of the record cache manager. - - flmRcaExit(); - - // Free the mutexes last of all. - - if (gv_FlmSysData.hQueryMutex != F_MUTEX_NULL) - { - f_mutexDestroy( &gv_FlmSysData.hQueryMutex); - } - - if (gv_FlmSysData.hShareMutex != F_MUTEX_NULL) - { - f_mutexDestroy( &gv_FlmSysData.hShareMutex); - } - - if (gv_FlmSysData.hFileHdlMutex != F_MUTEX_NULL) - { - f_mutexDestroy( &gv_FlmSysData.hFileHdlMutex); - } - - if (gv_FlmSysData.hServerLockMgrMutex != F_MUTEX_NULL) - { - f_mutexDestroy( &gv_FlmSysData.hServerLockMgrMutex); - } - - if (gv_FlmSysData.HttpConfigParms.hMutex != F_MUTEX_NULL) - { - f_mutexDestroy( &gv_FlmSysData.HttpConfigParms.hMutex); - } - - - // Free up callbacks that have been registered for events. - - for (iEventCategory = 0; - iEventCategory < F_MAX_EVENT_CATEGORIES; - iEventCategory++) - { - if (gv_FlmSysData.EventHdrs [iEventCategory].hMutex != F_MUTEX_NULL) - { - while (gv_FlmSysData.EventHdrs [iEventCategory].pEventCBList) - { - flmFreeEvent( - gv_FlmSysData.EventHdrs [iEventCategory].pEventCBList, - gv_FlmSysData.EventHdrs [iEventCategory].hMutex, - &gv_FlmSysData.EventHdrs [iEventCategory].pEventCBList); - } - f_mutexDestroy( &gv_FlmSysData.EventHdrs [iEventCategory].hMutex); - } - } - - // Free (release) FLAIM's File Shared File System Object. - - if (gv_FlmSysData.pFileSystem) - { - FLMUINT uiRefCnt = gv_FlmSysData.pFileSystem->Release(); - - // No one else should have a reference to the file system - // after this point. - -#ifdef FLM_DEBUG - flmAssert( 0 == uiRefCnt); -#else - // Quiet the compiler about the unused variable - (void)uiRefCnt; -#endif - gv_FlmSysData.pFileSystem = NULL; - } -#ifdef FLM_DBG_LOG - flmDbgLogExit(); -#endif - - // Free the serial number generator - - f_freeSerialNumberGenerator(); - -#if defined( FLM_NLM) - nssUninitialize(); -#endif - - // Release the logger (if any) - - if( gv_FlmSysData.pLogger) - { - gv_FlmSysData.pLogger->lockLogger(); - if( gv_FlmSysData.pLogger->Release() >= 1) - { - gv_FlmSysData.pLogger->unlockLogger(); - } - gv_FlmSysData.pLogger = NULL; - } - - // Release the thread manager - - if( gv_FlmSysData.pThreadMgr) - { - gv_FlmSysData.pThreadMgr->Release(); - gv_FlmSysData.pThreadMgr = NULL; - } - - // Release the slab manager - - if( gv_FlmSysData.pSlabManager) - { - gv_FlmSysData.pSlabManager->Release(); - gv_FlmSysData.pSlabManager = NULL; - } - - // Shutdown NICI - -#ifdef FLM_USE_NICI - CCS_Shutdown(); -#endif - - // Memory cleanup needs to be last. - - f_memoryCleanup(); - -#ifdef FLM_NLM - if( gv_bNetWareStartupCalled) - { - f_netwareShutdown(); - gv_bNetWareStartupCalled = FALSE; - } -#endif -} - -/*API~*********************************************************************** -Desc : Shuts down FLAIM. -Notes: Allows itself to be called multiple times and even before FlmStartup - is called, or even if FlmStartup fails. Warning: May not handle - race conditions very well on platforms that do not support atomic - exchange. -*END************************************************************************/ -FLMEXP void FLMAPI FlmShutdown( void) -{ - flmLockSysData(); - flmCleanup(); - flmUnlockSysData(); -} - -/**************************************************************************** -Desc: This routine determines the hash bucket for a string. -****************************************************************************/ -FLMUINT flmStrHashBucket( - const char * pszStr, - FBUCKET * pHashTbl, - FLMUINT uiNumBuckets) -{ - FLMUINT uiHashIndex; - - if ((uiHashIndex = (FLMUINT)*pszStr) >= uiNumBuckets) - { - uiHashIndex -= uiNumBuckets; - } - - while( *pszStr) - { - if ((uiHashIndex = - (FLMUINT)((pHashTbl [uiHashIndex].uiHashValue) ^ (FLMUINT)(*pszStr))) >= - uiNumBuckets) - uiHashIndex -= uiNumBuckets; - pszStr++; - } - return( uiHashIndex); -} - -/**************************************************************************** -Desc: This routine determines the hash bucket for a binary array of - characters. -****************************************************************************/ -FLMUINT flmBinHashBucket( - void * pBuf, - FLMUINT uiBufLen, - FBUCKET * pHashTbl, - FLMUINT uiNumBuckets) -{ - FLMUINT uiHashIndex; - FLMBYTE * ptr = (FLMBYTE *)pBuf; - - if ((uiHashIndex = (FLMUINT)*ptr) >= uiNumBuckets) - uiHashIndex -= uiNumBuckets; - while (uiBufLen) - { - if ((uiHashIndex = - (FLMUINT)((pHashTbl [uiHashIndex].uiHashValue) ^ (FLMUINT)(*ptr))) >= - uiNumBuckets) - uiHashIndex -= uiNumBuckets; - ptr++; - uiBufLen--; - } - return( uiHashIndex); -} - -/**************************************************************************** -Desc: This routine links a notify request into a notification list and - then waits to be notified that the event has occurred. -Notes: - This routine assumes that the shared mutex is locked and that - it is supposed to unlock it. -****************************************************************************/ -RCODE flmWaitNotifyReq( - F_MUTEX hMutex, - FNOTIFY ** ppNotifyListRV, /* Pointer to the head of a notify - list where the new notify - request should be linked into. */ - void * UserData /* Other user data that the notifier - can use to pass other information - to the waiter. */ - ) -{ - FNOTIFY * pNotify = NULL; - RCODE TempRc; - RCODE rc = FERR_OK; - F_SEM hSem; - - /* First create a notify request and link it into the list. */ - - if (RC_OK( rc = f_calloc( (FLMUINT)(sizeof( FNOTIFY)), &pNotify))) - { - - /* Allocate a semaphore for the notify request. */ - - pNotify->uiThreadId = f_threadId(); - pNotify->hSem = F_SEM_NULL; - rc = f_semCreate( &pNotify->hSem); - } - if (RC_BAD( rc)) - { - if (pNotify) - { - if (pNotify->hSem != F_SEM_NULL) - { - f_semDestroy( &pNotify->hSem); - } - f_free( &pNotify); - } - goto Exit; - } - pNotify->pRc = &rc; - pNotify->UserData = UserData; - pNotify->pNext = *ppNotifyListRV; - *ppNotifyListRV = pNotify; - hSem = pNotify->hSem; - - /* Unlock the mutex and wait on the semaphore. */ - - f_mutexUnlock( hMutex); - if (RC_BAD( TempRc = f_semWait( hSem, F_SEM_WAITFOREVER))) - { - rc = TempRc; - } - - /* Free the semaphore and the notify structure. */ - - f_semDestroy( &hSem); - f_free( &pNotify); - - // Relock the mutex - - f_mutexLock( hMutex); - -Exit: - - return( rc); -} - -/**************************************************************************** -Desc: This routine links an FFILE structure to its name hash bucket. - NOTE: This function assumes that the global mutex has been - locked. -****************************************************************************/ -RCODE flmLinkFileToBucket( - FFILE * pFile /* File to be linked to its name hash bucket. */ - ) -{ - RCODE rc = FERR_OK; - FFILE * pTmpFile; - FBUCKET * pBucket; - FLMUINT uiBucket; - char szDbPathStr [F_PATH_MAX_SIZE]; - - pBucket = gv_FlmSysData.pFileHashTbl; - - // Normalize the path to a string before hashing on it. - - if (RC_BAD( rc = f_pathToStorageString( pFile->pszDbPath, szDbPathStr))) - { - goto Exit; - } - - uiBucket = flmStrHashBucket( szDbPathStr, pBucket, FILE_HASH_ENTRIES); - pBucket = &pBucket [uiBucket]; - if (pBucket->pFirstInBucket) - { - pTmpFile = (FFILE *)pBucket->pFirstInBucket; - pTmpFile->pPrev = pFile; - } - pFile->uiBucket = uiBucket; - pFile->pPrev = (FFILE *)NULL; - pFile->pNext = (FFILE *)pBucket->pFirstInBucket; - pBucket->pFirstInBucket = pFile; - -Exit: - - return( rc); -} - - -/**************************************************************************** -Desc: This routine unlinks an FFILE structure from its name hash bucket. - NOTE: This function assumes that the global mutex has been - locked. -****************************************************************************/ -FSTATIC void flmUnlinkFileFromBucket( - FFILE * pFile /* Pointer to file that is to be unlinked from - its name hash bucket. */ - ) -{ - if (pFile->uiBucket != 0xFFFF) - { - if (pFile->pPrev) - { - pFile->pPrev->pNext = pFile->pNext; - } - else - { - gv_FlmSysData.pFileHashTbl [pFile->uiBucket].pFirstInBucket = pFile->pNext; - } - if (pFile->pNext) - { - pFile->pNext->pPrev = pFile->pPrev; - } - pFile->uiBucket = 0xFFFF; - } -} - -/**************************************************************************** -Desc: This routine links an FFILE structure to the unused list. - NOTE: This function assumes that the global mutex has been - locked. -****************************************************************************/ -void flmLinkFileToNUList( - FFILE * pFile, - FLMBOOL bQuickTimeout) -{ - - if( !bQuickTimeout) - { - pFile->pPrevNUFile = NULL; - if ((pFile->pNextNUFile = gv_FlmSysData.pMrnuFile) == NULL) - { - gv_FlmSysData.pLrnuFile = pFile; - } - else - { - pFile->pNextNUFile->pPrevNUFile = pFile; - } - - gv_FlmSysData.pMrnuFile = pFile; - pFile->uiZeroUseCountTime = (FLMUINT)FLM_GET_TIMER(); - } - else - { - pFile->pNextNUFile = NULL; - if ((pFile->pPrevNUFile = gv_FlmSysData.pLrnuFile) == NULL) - { - gv_FlmSysData.pMrnuFile = pFile; - } - else - { - pFile->pPrevNUFile->pNextNUFile = pFile; - } - - gv_FlmSysData.pLrnuFile = pFile; - pFile->uiZeroUseCountTime = 0; - } - - pFile->uiFlags |= DBF_IN_NU_LIST; - - if (pFile->pRfl) - { - pFile->pRfl->closeFile(); - } -} - -/**************************************************************************** -Desc: This routine unlinks an FFILE structure from the unused list. - NOTE: This function assumes that the global mutex has been - locked. -****************************************************************************/ -void flmUnlinkFileFromNUList( - FFILE * pFile /* File to be unlinked from unused list. */ - ) -{ - if (pFile->uiFlags & DBF_IN_NU_LIST) - { - if (!pFile->pPrevNUFile) - { - gv_FlmSysData.pMrnuFile = pFile->pNextNUFile; - } - else - { - pFile->pPrevNUFile->pNextNUFile = pFile->pNextNUFile; - } - if (!pFile->pNextNUFile) - { - gv_FlmSysData.pLrnuFile = pFile->pPrevNUFile; - } - else - { - pFile->pNextNUFile->pPrevNUFile = pFile->pPrevNUFile; - } - pFile->pPrevNUFile = pFile->pNextNUFile = (FFILE *)NULL; - pFile->uiFlags &= ~(DBF_IN_NU_LIST); - } -} - -/**************************************************************************** -Desc: This routine checks unused structures to see if any have been unused - longer than the maximum unused time. If so, it frees them up. -Note: This routine assumes that the calling routine has locked the global - mutex prior to calling this routine. The mutex may be unlocked and - re-locked by one of the called routines. -****************************************************************************/ -void flmCheckNUStructs( - FLMUINT uiCurrTime) -{ - FFILE * pFile; - - if (!uiCurrTime) - { - uiCurrTime = FLM_GET_TIMER(); - } - - // Look for unused FFILEs - - pFile = gv_FlmSysData.pLrnuFile; - for (;;) - { - // Break out of the loop as soon as we discover an unused FFILE - // structure which has not been unused the maximum number of seconds. - - if (pFile && - (FLM_ELAPSED_TIME( uiCurrTime, pFile->uiZeroUseCountTime) >= - gv_FlmSysData.uiMaxUnusedTime || !pFile->uiZeroUseCountTime)) - { - - // Remove the FFILE from memory. - - flmFreeFile( pFile); - - // flmFreeFile may have unlocked (and re-locked the global mutex, - // so we need to start at the beginning of the list again. - - // Need to unlock the mutex here in case another thread is in - // the process of closing the last FFILE. If we hang on to - // the mutex, it will never be able to get back in and finish - // the job. - - f_mutexUnlock( gv_FlmSysData.hShareMutex); - f_yieldCPU(); - f_mutexLock( gv_FlmSysData.hShareMutex); - pFile = gv_FlmSysData.pLrnuFile; - } - else - { - break; - } - } - - // Look for unused file handles - - if( gv_FlmSysData.pFileHdlMgr) - { - gv_FlmSysData.pFileHdlMgr->CheckAgedItems( - gv_FlmSysData.pFileHdlMgr->GetMaxAvailTime()); - } -} - -/**************************************************************************** -Desc: This routine unlinks an FDICT structure from its FFILE structure and - then frees the FDICT structure. - NOTE: This routine assumes that the global mutex is locked. -****************************************************************************/ -void flmUnlinkDict( - FDICT * pDict /* FDICT that is to be unlinked from its FFFILE - structure. */ - ) -{ - /* - Now unlink the local dictionary from its file - if it is connected - to one. - */ - - if (pDict->pFile) - { - if (pDict->pPrev) - { - pDict->pPrev->pNext = pDict->pNext; - } - else - { - pDict->pFile->pDictList = pDict->pNext; - } - if (pDict->pNext) - { - pDict->pNext->pPrev = pDict->pPrev; - } - } - - /* Finally, free the local dictionary and its associated tables. */ - - flmFreeDict( pDict); -} - -/**************************************************************************** -Desc: This routine links an FDB structure to an FFILE structure. - NOTE: This routine assumes that the global mutex has been - locked. -****************************************************************************/ -RCODE flmLinkFdbToFile( - FDB * pDb, /* FDB that is to be linked to an FFILE - structure. */ - FFILE * pFile /* Pointer to FFILE structure the FDB is to be - linked to. */ - ) -{ - RCODE rc = FERR_OK; - - /* - If the use count on the file used to be zero, unlink it from the - unused list. - */ - - flmAssert( !pDb->pFile); - pDb->pPrevForFile = NULL; - if ((pDb->pNextForFile = pFile->pFirstDb) != NULL) - { - pFile->pFirstDb->pPrevForFile = pDb; - } - pFile->pFirstDb = pDb; - pDb->pFile = pFile; - if (++pFile->uiUseCount == 1) - { - flmUnlinkFileFromNUList( pFile); - } - if (pDb->uiFlags & FDB_INTERNAL_OPEN) - { - pFile->uiInternalUseCount++; - } - - /* - Allocate the super file object - */ - - if (!pDb->pSFileHdl) - { - if( (pDb->pSFileHdl = f_new F_SuperFileHdl) == NULL) - { - rc = RC_SET( FERR_MEM); - goto Exit; - } - - /* - Set up the super file - */ - - if( RC_BAD( rc = pDb->pSFileHdl->Setup( pFile->pFileIdList, - pFile->pszDbPath, - pFile->pszDataDir))) - { - goto Exit; - } - - if( pFile->pECacheMgr) - { - pDb->pSFileHdl->setECacheMgr( pFile->pECacheMgr); - } - - if( pFile->FileHdr.uiVersionNum) - { - pDb->pSFileHdl->SetBlockSize( pFile->FileHdr.uiBlockSize); - pDb->pSFileHdl->SetDbVersion( pFile->FileHdr.uiVersionNum); - } - } - -Exit: - - return( rc); -} - -/**************************************************************************** -Desc: This routine unlinks an FDB structure from its FFILE structure. - NOTE: This routine assumes that the global mutex has been - locked. -****************************************************************************/ -void flmUnlinkFdbFromFile( - FDB * pDb) -{ - FFILE * pFile; - - if ((pFile = pDb->pFile) != NULL) - { - - // Unlink the FDB from the FFILE. - - if (pDb->pNextForFile) - { - pDb->pNextForFile->pPrevForFile = pDb->pPrevForFile; - } - if (pDb->pPrevForFile) - { - pDb->pPrevForFile->pNextForFile = pDb->pNextForFile; - } - else - { - pFile->pFirstDb = pDb->pNextForFile; - } - pDb->pNextForFile = pDb->pPrevForFile = NULL; - pDb->pFile = NULL; - - // Decrement use counts in the FFILE. - - if (pDb->uiFlags & FDB_INTERNAL_OPEN) - { - flmAssert( pFile->uiInternalUseCount); - pFile->uiInternalUseCount--; - } - - // If the use count goes to zero on the file, put the file - // into the unused list. - - flmAssert( pFile->uiUseCount); - if (!(--pFile->uiUseCount)) - { - // If the "must close" flag is set, it indicates that - // the FFILE is being forced to close. Put the FFILE in - // the NU list, but specify that it should be quickly - // timed-out. - - flmLinkFileToNUList( pFile, pFile->bMustClose); - } - } -} - -/**************************************************************************** -Desc: This routine functions as a thread. It monitors open files and - frees up files which have been closed longer than the maximum - close time. -****************************************************************************/ -RCODE flmMonitor( - F_Thread * pThread) -{ - FLMUINT uiLastUnusedCleanupTime = 0; - FLMUINT uiLastRCacheCleanupTime = 0; - FLMUINT uiLastSCacheCleanupTime = 0; - FLMUINT uiCurrTime; - FLMUINT uiMaxLockTime; -#ifdef FLM_CAN_GET_PHYS_MEM - FLMUINT uiLastCacheAdjustTime = 0; -#endif - - FLM_MILLI_TO_TIMER_UNITS( 100, uiMaxLockTime); - - for (;;) - { - - /* See if we should shut down. */ - - if( pThread->getShutdownFlag()) - { - break; - } - - uiCurrTime = FLM_GET_TIMER(); - - // Check the not used stuff and lock timeouts. - - if ( FLM_ELAPSED_TIME( uiCurrTime, uiLastUnusedCleanupTime) >= - gv_FlmSysData.uiUnusedCleanupInterval || - (gv_FlmSysData.pLrnuFile && - !gv_FlmSysData.pLrnuFile->uiZeroUseCountTime)) - { - // See if any unused structures have bee unused longer than the - // maximum unused time. Free them if they have. - // May unlock and re-lock the global mutex. - - f_mutexLock( gv_FlmSysData.hShareMutex); - flmCheckNUStructs( 0); - f_mutexUnlock( gv_FlmSysData.hShareMutex); - - // Reset the timer - - uiCurrTime = uiLastUnusedCleanupTime = FLM_GET_TIMER(); - } - - // Call the lock manager to check timeouts. It is critial - // that this routine be called on a regular interval to - // timeout lock waiters that have expired. - - gv_FlmSysData.pServerLockMgr->CheckLockTimeouts( FALSE); - - // Check the adjusting cache limit - -#ifdef FLM_CAN_GET_PHYS_MEM - if ((gv_FlmSysData.bDynamicCacheAdjust) && - (FLM_ELAPSED_TIME( uiCurrTime, uiLastCacheAdjustTime) >= - gv_FlmSysData.uiCacheAdjustInterval)) - { - FLMUINT uiCacheBytes; - - f_mutexLock( gv_FlmSysData.hShareMutex); - f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); - - // Make sure the dynamic adjust flag is still set. - - if ((gv_FlmSysData.bDynamicCacheAdjust) && - (FLM_ELAPSED_TIME( uiCurrTime, uiLastCacheAdjustTime) >= - gv_FlmSysData.uiCacheAdjustInterval)) - { - uiCacheBytes = flmGetCacheBytes( gv_FlmSysData.uiCacheAdjustPercent, - gv_FlmSysData.uiCacheAdjustMin, - gv_FlmSysData.uiCacheAdjustMax, - gv_FlmSysData.uiCacheAdjustMinToLeave, TRUE, - gv_FlmSysData.SCacheMgr.Usage.uiTotalBytesAllocated + - gv_FlmSysData.RCacheMgr.pRCacheAlloc->getTotalBytesAllocated()); - (void)flmSetCacheLimits( uiCacheBytes, FALSE); - } - f_mutexUnlock( gv_FlmSysData.hShareMutex); - f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); - uiCurrTime = uiLastCacheAdjustTime = FLM_GET_TIMER(); - } -#endif - - // See if block cache should be cleaned up - - if ((gv_FlmSysData.uiCacheCleanupInterval) && - (FLM_ELAPSED_TIME( uiCurrTime, uiLastSCacheCleanupTime) >= - gv_FlmSysData.uiCacheCleanupInterval)) - { - ScaCleanupCache( uiMaxLockTime); - uiCurrTime = uiLastSCacheCleanupTime = FLM_GET_TIMER(); - } - - // See if record cache should be cleaned up - - if( (gv_FlmSysData.uiCacheCleanupInterval) && - (FLM_ELAPSED_TIME( uiCurrTime, uiLastRCacheCleanupTime) >= - gv_FlmSysData.uiCacheCleanupInterval)) - { - flmRcaCleanupCache( uiMaxLockTime, FALSE); - uiCurrTime = uiLastRCacheCleanupTime = FLM_GET_TIMER(); - } - - // Cleanup old sessions - - if( gv_FlmSysData.pSessionMgr) - { - gv_FlmSysData.pSessionMgr->timeoutInactiveSessions( - MAX_SESSION_INACTIVE_SECS, FALSE); - } - - f_sleep( 1000); - } - - return( FERR_OK); -} - -/*API~*********************************************************************** -Desc : Registers a callback function to receive events. -*END************************************************************************/ -FLMEXP RCODE FLMAPI FlmRegisterForEvent( - FEventCategory eCategory, - FEVENT_CB fnEventCB, - void * pvAppData, - HFEVENT * phEventRV) -{ - RCODE rc = FERR_OK; - FEVENT * pEvent; - - *phEventRV = HFEVENT_NULL; - - // Make sure it is a legal event category to register for. - - if (eCategory >= F_MAX_EVENT_CATEGORIES) - { - rc = RC_SET( FERR_NOT_IMPLEMENTED); - goto Exit; - } - - // Allocate an event structure - - if (RC_BAD( rc = f_calloc( (FLMUINT)(sizeof( FEVENT)), &pEvent))) - { - goto Exit; - } - *phEventRV = (HFEVENT)pEvent; - - // Initialize the structure members and linkt to the - // list of events off of the event category. - - pEvent->eCategory = eCategory; - pEvent->fnEventCB = fnEventCB; - pEvent->pvAppData = pvAppData; - // pEvent->pPrev = NULL; // done by flmAlloc above. - - // Mutex should be locked to link into list. - - f_mutexLock( gv_FlmSysData.EventHdrs [eCategory].hMutex); - if ((pEvent->pNext = - gv_FlmSysData.EventHdrs [eCategory].pEventCBList) != NULL) - { - pEvent->pNext->pPrev = pEvent; - } - gv_FlmSysData.EventHdrs [eCategory].pEventCBList = pEvent; - f_mutexUnlock( gv_FlmSysData.EventHdrs [eCategory].hMutex); - -Exit: - return( rc); -} - -/*API~*********************************************************************** -Desc : Deregisters a callback function that was registered to receive events. -*END************************************************************************/ -FLMEXP void FLMAPI FlmDeregisterForEvent( - HFEVENT * phEventRV) -{ - if (phEventRV && *phEventRV != HFEVENT_NULL) - { - FEVENT * pEvent = (FEVENT *)(*phEventRV); - - if (pEvent->eCategory < F_MAX_EVENT_CATEGORIES) - { - flmFreeEvent( pEvent, - gv_FlmSysData.EventHdrs [pEvent->eCategory].hMutex, - &gv_FlmSysData.EventHdrs [pEvent->eCategory].pEventCBList); - } - *phEventRV = HFEVENT_NULL; - } -} - -/**************************************************************************** -Desc: This routine does an event callback. Note that the mutex is - locked during the callback. -****************************************************************************/ -void flmDoEventCallback( - FEventCategory eCategory, - FEventType eEventType, - void * pvEventData1, - void * pvEventData2) -{ - FEVENT * pEvent; - - f_mutexLock( gv_FlmSysData.EventHdrs [eCategory].hMutex); - pEvent = gv_FlmSysData.EventHdrs [eCategory].pEventCBList; - while (pEvent) - { - (*pEvent->fnEventCB)( eEventType, pEvent->pvAppData, - pvEventData1, - pvEventData2); - pEvent = pEvent->pNext; - } - f_mutexUnlock( gv_FlmSysData.EventHdrs [eCategory].hMutex); -} - -/**************************************************************************** -Desc: This routine sets the "must close" flags on the FFILE and its FDBs -****************************************************************************/ -void flmSetMustCloseFlags( - FFILE * pFile, - RCODE rcMustClose, - FLMBOOL bMutexLocked) -{ - FDB * pTmpDb; - - if( !bMutexLocked) - { - f_mutexLock( gv_FlmSysData.hShareMutex); - } - - if( !pFile->bMustClose) - { - pFile->bMustClose = TRUE; - pFile->rcMustClose = rcMustClose; - pTmpDb = pFile->pFirstDb; - while( pTmpDb) - { - pTmpDb->bMustClose = TRUE; - pTmpDb = pTmpDb->pNextForFile; - } - - // Log a message indicating why the "must close" flag has been - // set. Calling flmCheckFFileState with the bMustClose flag - // already set to TRUE will cause a message to be logged. - - (void)flmCheckFFileState( pFile); - } - - if( !bMutexLocked) - { - f_mutexUnlock( gv_FlmSysData.hShareMutex); - } -} - -/**************************************************************************** -Desc: Constructor -****************************************************************************/ -F_Session::F_Session() -{ - m_pSessionMgr = NULL; - m_uiThreadId = 0; - m_uiThreadLockCount = 0; - m_hMutex = F_MUTEX_NULL; - m_pNotifyList = NULL; - m_pPrev = NULL; - m_pNext = NULL; - m_pNameTable = NULL; - m_uiNameTableFFileId = 0; - m_uiDictSeqNum = 0; - m_uiLastUsed = FLM_GET_TIMER(); - m_uiNextToken = FLM_GET_TIMER(); - m_pXmlImport = NULL; - m_pXmlExport = NULL; - m_pDbTable = NULL; - f_memset( m_ucKey, 0, sizeof( m_ucKey)); -} - -/**************************************************************************** -Desc: Destructor -****************************************************************************/ -F_Session::~F_Session() -{ - flmAssert( !m_pPrev); - flmAssert( !m_pNext); - flmAssert( !m_ui32RefCnt); - flmAssert( !m_uiThreadLockCount); - - // Wake up any waiters - - signalLockWaiters( FERR_FAILURE, FALSE); - - // Free the session mutex - - if( m_hMutex != F_MUTEX_NULL) - { - f_mutexDestroy( &m_hMutex); - } - - // Clean up any database objects - - if( m_pDbTable) - { - m_pDbTable->Release(); - } - - // Free the name table - - if( m_pNameTable) - { - m_pNameTable->Release(); - } - - // Free XML import / export - - if( m_pXmlImport) - { - m_pXmlImport->Release(); - } - - if( m_pXmlExport) - { - m_pXmlExport->Release(); - } -} - -/**************************************************************************** -Desc: Signals the next thread waiting to acquire the lock on the session - if rc == FERR_OK. Otherwise, signals all waiting threads. -****************************************************************************/ -void F_Session::signalLockWaiters( - RCODE rc, - FLMBOOL bMutexLocked) -{ - F_SEM hSem; - - if( m_pNotifyList) - { - if( !bMutexLocked) - { - f_mutexLock( m_hMutex); - } - - while( m_pNotifyList) - { - *(m_pNotifyList->pRc) = rc; - hSem = m_pNotifyList->hSem; - m_pNotifyList = m_pNotifyList->pNext; - f_semSignal( hSem); - if( RC_OK( rc)) - { - break; - } - } - - if( !bMutexLocked) - { - f_mutexUnlock( m_hMutex); - } - } -} - -/**************************************************************************** -Desc: Configures a session for use. -****************************************************************************/ -RCODE F_Session::setupSession( - F_SessionMgr * pSessionMgr) -{ - RCODE rc = FERR_OK; - - flmAssert( m_hMutex == F_MUTEX_NULL); - flmAssert( pSessionMgr); - - if( RC_BAD( rc = f_mutexCreate( &m_hMutex))) - { - goto Exit; - } - - if( (m_pXmlImport = f_new F_XMLImport) == NULL) - { - rc = RC_SET( FERR_MEM); - goto Exit; - } - - if( RC_BAD( rc = m_pXmlImport->setup())) - { - goto Exit; - } - - if( (m_pXmlExport = f_new F_XMLExport) == NULL) - { - rc = RC_SET( FERR_MEM); - goto Exit; - } - - if( RC_BAD( rc = m_pXmlExport->setup())) - { - goto Exit; - } - - // Allocate the object table - - if( (m_pDbTable = f_new F_HashTable) == NULL) - { - rc = RC_SET( FERR_MEM); - goto Exit; - } - - if( RC_BAD( rc = m_pDbTable->setupHashTable( FALSE, 16, - pSessionMgr->getCRCTable()))) - { - goto Exit; - } - - m_pSessionMgr = pSessionMgr; - -Exit: - - return( rc); -} - -/**************************************************************************** -Desc: Adds a database handle to the session's list of handles. Once added - to the session, the calling code should not close the handle directly. -****************************************************************************/ -RCODE F_Session::addDbHandle( - HFDB hDb, - char * pucKey) -{ - RCODE rc = FERR_OK; - F_SessionDb * pSessionDb = NULL; - void * pvKey; - FLMUINT uiKeyLen; - - if( (pSessionDb = f_new F_SessionDb) == NULL) - { - rc = RC_SET( FERR_MEM); - goto Exit; - } - - if( RC_BAD( rc = pSessionDb->setupSessionDb( this, hDb))) - { - goto Exit; - } - - if( RC_BAD( rc = m_pDbTable->addObject( pSessionDb))) - { - goto Exit; - } - - if( pucKey) - { - pvKey = pSessionDb->getKey( &uiKeyLen); - flmAssert( uiKeyLen == F_SESSION_DB_KEY_LEN); - f_memcpy( pucKey, (FLMBYTE *)pvKey, uiKeyLen); - } - - pSessionDb->Release(); - pSessionDb = NULL; - -Exit: - - if( pSessionDb) - { - pSessionDb->m_hDb = HFDB_NULL; - pSessionDb->Release(); - } - - return( rc); -} - -/**************************************************************************** -Desc: Closes the specific database identified by the passed-in key -****************************************************************************/ -void F_Session::closeDb( - const char * pucKey) -{ - (void)m_pDbTable->getObject( (void *)pucKey, F_SESSION_DB_KEY_LEN, - NULL, TRUE); -} - -/**************************************************************************** -Desc: Gets a specific database handle from the session given the passed-in - key -****************************************************************************/ -RCODE F_Session::getDbHandle( - const char * pucKey, - HFDB * phDb) -{ - RCODE rc = FERR_OK; - F_SessionDb * pSessionDb = NULL; - F_HashObject * pObject; - - *phDb = HFDB_NULL; - - if( RC_BAD( rc = m_pDbTable->getObject( (void *)pucKey, F_SESSION_DB_KEY_LEN, - &pObject, FALSE))) - { - if( rc == FERR_NOT_FOUND) - { - rc = RC_SET( FERR_BAD_HDL); - } - - goto Exit; - } - - pSessionDb = (F_SessionDb *)pObject; - *phDb = pSessionDb->m_hDb; - -Exit: - - if( pSessionDb) - { - pSessionDb->Release(); - } - - return( rc); -} - -/**************************************************************************** -Desc: Returns the next database handle in the global list of handles managed - by the session. -****************************************************************************/ -RCODE F_Session::getNextDb( - F_SessionDb ** ppSessionDb) -{ - F_HashObject * pObject = *ppSessionDb; - RCODE rc = FERR_OK; - - if( RC_BAD( rc = m_pDbTable->getNextObjectInGlobal( &pObject))) - { - goto Exit; - } - - *ppSessionDb = (F_SessionDb *)pObject; - -Exit: - - return( rc); -} - -/**************************************************************************** -Desc: Releases any session resources associated with the specified FFILE -****************************************************************************/ -void F_Session::releaseFileResources( - FFILE * pFile) -{ - F_HashObject * pObject; - F_HashObject * pNextObject; - F_SessionDb * pSessionDb; - - // Close all database handles with the specified FFILE - - pObject = NULL; - if( RC_BAD( m_pDbTable->getNextObjectInGlobal( &pObject))) - { - goto Exit; - } - - while( pObject) - { - if( (pNextObject = pObject->getNextInGlobal()) != NULL) - { - pNextObject->AddRef(); - } - - if( pObject->objectType() == HASH_DB_OBJ) - { - pSessionDb = (F_SessionDb *)pObject; - - if( ((FDB *)pSessionDb->getDbHandle())->pFile == pFile) - { - closeDb( (const char *)pSessionDb->getKey()); - } - } - pObject->Release(); - pObject = pNextObject; - } - -Exit: - - return; -} - -/**************************************************************************** -Desc: Returns a pointer to a name table generated based on the supplied - database handle -****************************************************************************/ -RCODE F_Session::getNameTable( - HFDB hDb, - F_NameTable ** ppNameTable) -{ - FLMUINT uiSeq; - FLMUINT uiFFileId; - RCODE rc = FERR_OK; - - if( !m_pNameTable) - { - if( (m_pNameTable = f_new F_NameTable) == NULL) - { - rc = RC_SET( FERR_MEM); - goto Exit; - } - } - - if( RC_BAD( rc = FlmDbGetConfig( hDb, - FDB_GET_DICT_SEQ_NUM, (void *)&uiSeq))) - { - goto Exit; - } - - if( RC_BAD( rc = FlmDbGetConfig( hDb, - FDB_GET_FFILE_ID, (void *)&uiFFileId))) - { - goto Exit; - } - - // If the database handle does not reference the same - // database or dictionary as the last time the name table - // was refreshed, we need to re-populate the table. - - if( uiSeq != m_uiDictSeqNum || - m_uiNameTableFFileId != uiFFileId) - { - if( RC_BAD( rc = m_pNameTable->setupFromDb( hDb))) - { - goto Exit; - } - - m_uiDictSeqNum = uiSeq; - m_uiNameTableFFileId = uiFFileId; - } - - *ppNameTable = m_pNameTable; - -Exit: - - return( rc); -} - -/**************************************************************************** -Desc: Returns a pointer to a name table generated based on the supplied - FFILE. -****************************************************************************/ -RCODE F_Session::getNameTable( - FFILE * pFile, - F_NameTable ** ppNameTable) -{ - FDB * pDb = NULL; - RCODE rc = FERR_OK; - - // Temporarily open the database - - if( RC_BAD( rc = flmOpenFile( pFile, NULL, NULL, NULL, 0, - TRUE, NULL, NULL, - pFile->pszDbPassword, &pDb))) - { - goto Exit; - } - - // Get the name table - - if( RC_BAD( rc = getNameTable( (HFDB)pDb, ppNameTable))) - { - goto Exit; - } - -Exit: - - if( pDb) - { - (void) FlmDbClose( (HFDB *)&pDb); - } - - return( rc); -} - -/**************************************************************************** -Desc: Returns a unique token generated by the session manager. Tokens are - used to help make handles passed to clients unique across server - executions -****************************************************************************/ -FLMUINT F_Session::getNextToken( void) -{ - return( m_pSessionMgr->getNextToken()); -} - -/**************************************************************************** -Desc: Locks a session for use by a thread. If bWait is TRUE, the thread - will be put into a queue until the lock can be granted. This routine - can be called multiple times by the same thread, as long as - unlockSession is called a corresponding number of times. -****************************************************************************/ -RCODE F_Session::lockSession( - FLMBOOL bWait) -{ - RCODE rc = FERR_OK; - - flmAssert( m_ui32RefCnt); - f_mutexLock( m_hMutex); - - if( m_uiThreadId && m_uiThreadId != f_threadId()) - { - if( !bWait) - { - rc = RC_SET( FERR_TIMEOUT); - goto Exit; - } - - if( RC_BAD( rc = flmWaitNotifyReq( m_hMutex, &m_pNotifyList, NULL))) - { - goto Exit; - } - } - - m_uiThreadId = f_threadId(); - m_uiThreadLockCount++; - -Exit: - - f_mutexUnlock( m_hMutex); - return( rc); -} - -/**************************************************************************** -Desc: Releases a thread's lock on a session. -****************************************************************************/ -void F_Session::unlockSession() -{ - F_SEM hSem; - - flmAssert( m_ui32RefCnt); - - f_mutexLock( m_hMutex); - if( m_uiThreadId != f_threadId()) - { - flmAssert( 0); - } - else - { - if( --m_uiThreadLockCount == 0) - { - m_uiThreadId = 0; - - if( m_pNotifyList) - { - *(m_pNotifyList->pRc) = FERR_OK; - hSem = m_pNotifyList->hSem; - m_pNotifyList = m_pNotifyList->pNext; - f_semSignal( hSem); - } - } - - m_uiLastUsed = FLM_GET_TIMER(); - } - f_mutexUnlock( m_hMutex); -} - -/**************************************************************************** -Desc: Gets the session object's hash key -****************************************************************************/ -void * F_Session::getKey( - FLMUINT * puiKeyLen) -{ - if( puiKeyLen) - { - *puiKeyLen = (FLMUINT)sizeof( m_ucKey); - } - return( (void *)(&m_ucKey[ 0])); -} - -/**************************************************************************** -Desc: Adds a reference to the session object. The mutex is locked prior - to incrementing the count since multiple threads are allowed to - acquire a pointer to the object. However, they shouldn't use the - object w/o first locking it via a call to lockSession. -****************************************************************************/ -FLMUINT F_Session::AddRef( void) -{ - FLMUINT uiRefCnt; - - f_mutexLock( m_hMutex); - flmAssert( m_ui32RefCnt); - uiRefCnt = F_Base::AddRef(); - f_mutexUnlock( m_hMutex); - - return( uiRefCnt); -} - -/**************************************************************************** -Desc: Decrements the objects use count -****************************************************************************/ -FLMUINT F_Session::Release( void) -{ - FLMUINT uiRefCnt; - - flmAssert( m_ui32RefCnt); - - f_mutexLock( m_hMutex); - if( (uiRefCnt = --m_ui32RefCnt) == 0) - { - f_mutexUnlock( m_hMutex); - delete this; - return( uiRefCnt); - } - f_mutexUnlock( m_hMutex); - - return( uiRefCnt); -} - -/**************************************************************************** -Desc: Destructor -****************************************************************************/ -F_SessionMgr::~F_SessionMgr() -{ - if( m_pSessionTable) - { - shutdownSessions(); - m_pSessionTable->Release(); - } - - if( m_hMutex != F_MUTEX_NULL) - { - f_mutexDestroy( &m_hMutex); - } - - if( m_pCRCTable) - { - f_freeCRCTable( &m_pCRCTable); - } -} - -/**************************************************************************** -Desc: Releases all resources (database handles, etc.) in all sessions - if they are tied to the specified FFILE -****************************************************************************/ -void F_SessionMgr::releaseFileResources( - FFILE * pFile) -{ - F_Session * pSession; - F_HashObject * pObject; - FLMBOOL bMutexLocked = FALSE; - - if( m_hMutex == F_MUTEX_NULL) - { - goto Exit; - } - - f_mutexLock( m_hMutex); - bMutexLocked = TRUE; - - pObject = NULL; - if( RC_BAD( m_pSessionTable->getNextObjectInGlobal( &pObject))) - { - goto Exit; - } - - while( pObject) - { - flmAssert( pObject->objectType() == HASH_SESSION_OBJ); - - pSession = (F_Session *)pObject; - if( (pObject = pObject->getNextInGlobal()) != NULL) - { - pObject->AddRef(); - } - - if( RC_OK( pSession->lockSession())) - { - pSession->releaseFileResources( pFile); - pSession->unlockSession(); - } - pSession->Release(); - } - -Exit: - if (bMutexLocked) - { - f_mutexUnlock( m_hMutex); - } - - return; -} - -/**************************************************************************** -Desc: Shuts down all sessions being managed -****************************************************************************/ -void F_SessionMgr::shutdownSessions() -{ - F_Session * pSession; - F_HashObject * pObject; - FLMBOOL bMutexLocked = FALSE; - - if( m_hMutex == F_MUTEX_NULL) - { - goto Exit; - } - - f_mutexLock( m_hMutex); - bMutexLocked = TRUE; - - pObject = NULL; - if( RC_BAD( m_pSessionTable->getNextObjectInGlobal( &pObject))) - { - goto Exit; - } - - while( pObject) - { - flmAssert( pObject->objectType() == HASH_SESSION_OBJ); - - pSession = (F_Session *)pObject; - if( (pObject = pObject->getNextInGlobal()) != NULL) - { - pObject->AddRef(); - } - - if( RC_OK( pSession->lockSession())) - { - m_pSessionTable->removeObject( pSession); - pSession->signalLockWaiters( FERR_FAILURE, FALSE); - pSession->unlockSession(); - } - pSession->Release(); - } - -Exit: - if (bMutexLocked) - { - f_mutexUnlock( m_hMutex); - } - - return; -} - -/**************************************************************************** -Desc: Configures the session manager prior to use -****************************************************************************/ -RCODE F_SessionMgr::setupSessionMgr( void) -{ - RCODE rc = FERR_OK; - - flmAssert( m_hMutex == F_MUTEX_NULL); - - // Create the mutex - - if( RC_BAD( rc = f_mutexCreate( &m_hMutex))) - { - goto Exit; - } - - // Initialize the CRC table - - if( RC_BAD( rc = f_initCRCTable( &m_pCRCTable))) - { - goto Exit; - } - - // Create the session object table - - if( (m_pSessionTable = f_new F_HashTable) == NULL) - { - rc = RC_SET( FERR_MEM); - goto Exit; - } - - if( RC_BAD( rc = m_pSessionTable->setupHashTable( FALSE, - 128, m_pCRCTable))) - { - goto Exit; - } - -Exit: - - return( rc); -} - -/**************************************************************************** -Desc: Gets a session given a session key -****************************************************************************/ -RCODE F_SessionMgr::getSession( - const char * pszKey, - F_Session ** ppSession) -{ - RCODE rc = FERR_OK; - F_Session * pSession = NULL; - F_HashObject * pObject; - FLMBOOL bMutexLocked = FALSE; - - *ppSession = NULL; - - f_mutexLock( m_hMutex); - bMutexLocked = TRUE; - - if( RC_BAD( rc = m_pSessionTable->getObject( (void *)pszKey, - F_SESSION_KEY_LEN, &pObject, FALSE))) - { - goto Exit; - } - - // NOTE: getObject() does an addRef for the caller. - - pSession = (F_Session *)pObject; - - f_mutexUnlock( m_hMutex); - bMutexLocked = FALSE; - - if( RC_BAD( rc = pSession->lockSession())) - { - pSession->Release(); - goto Exit; - } - - *ppSession = pSession; - -Exit: - - if( bMutexLocked) - { - f_mutexUnlock( m_hMutex); - } - - return( rc); -} - -/**************************************************************************** -Desc: Unlocks and releases a session being used by a thread -****************************************************************************/ -void F_SessionMgr::releaseSession( - F_Session ** ppSession) -{ - (*ppSession)->unlockSession(); - (*ppSession)->Release(); - *ppSession = NULL; -} - -/**************************************************************************** -Desc: Creates a new session -****************************************************************************/ -RCODE F_SessionMgr::createSession( - F_Session ** ppSession) -{ - F_Session * pNewSession = NULL; - FLMBOOL bMutexLocked = FALSE; - RCODE rc = FERR_OK; - - if( (pNewSession = f_new F_Session) == NULL) - { - rc = RC_SET( FERR_MEM); - goto Exit; - } - - if( RC_BAD( rc = pNewSession->setupSession( this))) - { - goto Exit; - } - - f_mutexLock( m_hMutex); - bMutexLocked = TRUE; - - // Session ID - - f_sprintf( (char *)&pNewSession->m_ucKey[ 0], "%0*X", - (int)(sizeof( FLMUINT) * 2), - (unsigned)m_uiNextId++); - - // Token - - f_sprintf( (char *)&pNewSession->m_ucKey[ sizeof( FLMUINT) * 2], "%0*X", - (int)(sizeof( FLMUINT) * 2), - (unsigned)m_uiNextToken++); - - pNewSession->m_ucKey[ sizeof( pNewSession->m_ucKey) - 1] = 0; - - // Add the session to the table - - if( RC_BAD( rc = m_pSessionTable->addObject( - pNewSession))) - { - goto Exit; - } - - f_mutexUnlock( m_hMutex); - bMutexLocked = FALSE; - - if( RC_BAD( rc = pNewSession->lockSession())) - { - pNewSession->Release(); - goto Exit; - } - - *ppSession = pNewSession; - pNewSession = NULL; - -Exit: - - if( pNewSession) - { - pNewSession->Release(); - } - - if( bMutexLocked) - { - f_mutexUnlock( m_hMutex); - } - - return( rc); -} - -/**************************************************************************** -Desc: Kills any unused sessions that have been inactive for the specified - number of seconds -****************************************************************************/ -void F_SessionMgr::timeoutInactiveSessions( - FLMUINT uiInactiveSecs, - FLMBOOL bWaitForLocks) -{ - F_Session * pSession; - F_HashObject * pObject; - FLMUINT uiCurrTime; - FLMUINT uiElapTime; - FLMUINT uiElapSecs; - FLMBOOL bMutexLocked = FALSE; - - f_mutexLock( m_hMutex); - bMutexLocked = TRUE; - - pObject = NULL; - if( RC_BAD( m_pSessionTable->getNextObjectInGlobal( &pObject))) - { - goto Exit; - } - - while( pObject) - { - flmAssert( pObject->objectType() == HASH_SESSION_OBJ); - - pSession = (F_Session *)pObject; - if( (pObject = pObject->getNextInGlobal()) != NULL) - { - pObject->AddRef(); - } - - if( RC_OK( pSession->lockSession( bWaitForLocks))) - { - uiCurrTime = FLM_GET_TIMER(); - uiElapTime = FLM_ELAPSED_TIME( uiCurrTime, pSession->m_uiLastUsed); - FLM_TIMER_UNITS_TO_SECS( uiElapTime, uiElapSecs); - - if( !uiInactiveSecs || uiElapSecs >= uiInactiveSecs) - { - m_pSessionTable->removeObject( pSession); - pSession->signalLockWaiters( FERR_FAILURE, FALSE); - } - pSession->unlockSession(); - } - pSession->Release(); - } - -Exit: - - if( bMutexLocked) - { - f_mutexUnlock( m_hMutex); - } -} - -/**************************************************************************** -Desc: Constructor -****************************************************************************/ -F_HashTable::F_HashTable() -{ - m_hMutex = F_MUTEX_NULL; - m_pGlobalList = NULL; - m_ppHashTable = NULL; - m_uiBuckets = 0; - m_pCRCTable = NULL; - m_bOwnCRCTable = FALSE; -} - -/**************************************************************************** -Desc: Destructor -****************************************************************************/ -F_HashTable::~F_HashTable() -{ - F_HashObject * pCur; - F_HashObject * pNext; - - pCur = m_pGlobalList; - while( pCur) - { - pNext = pCur->m_pNextInGlobal; - unlinkObject( pCur); - pCur->Release(); - pCur = pNext; - } - - if( m_ppHashTable) - { - f_free( &m_ppHashTable); - } - - if( m_hMutex != F_MUTEX_NULL) - { - f_mutexDestroy( &m_hMutex); - } - - if( m_pCRCTable && m_bOwnCRCTable) - { - f_freeCRCTable( &m_pCRCTable); - } -} - -/**************************************************************************** -Desc: Configures the hash table prior to first use -****************************************************************************/ -RCODE F_HashTable::setupHashTable( - FLMBOOL bMultithreaded, - FLMUINT uiNumBuckets, - FLMUINT32 * pCRCTable) -{ - RCODE rc = FERR_OK; - - flmAssert( uiNumBuckets); - - // Create the hash table - - if( RC_BAD( rc = f_alloc( - sizeof( F_HashObject *) * uiNumBuckets, &m_ppHashTable))) - { - goto Exit; - } - - m_uiBuckets = uiNumBuckets; - f_memset( m_ppHashTable, 0, sizeof( F_HashObject *) * uiNumBuckets); - - if( bMultithreaded) - { - // Initialize the mutex - - if( RC_BAD( rc = f_mutexCreate( &m_hMutex))) - { - goto Exit; - } - } - - if( !pCRCTable) - { - // Initialize the CRC table - - if( RC_BAD( rc = f_initCRCTable( &m_pCRCTable))) - { - goto Exit; - } - m_bOwnCRCTable = TRUE; - } - else - { - m_pCRCTable = pCRCTable; - } - -Exit: - - return( rc); -} - -/**************************************************************************** -Desc: Retrieves an object from the hash table with the specified key. - This routine assumes the table's mutex has already been locked. - A reference IS NOT added to the object for the caller. -****************************************************************************/ -RCODE F_HashTable::findObject( - void * pvKey, - FLMUINT uiKeyLen, - F_HashObject ** ppObject) -{ - F_HashObject * pObject = NULL; - FLMUINT uiBucket; - FLMUINT32 ui32CRC = 0; - RCODE rc = FERR_OK; - - *ppObject = NULL; - - // Calculate the hash bucket and mutex offset - - uiBucket = getHashBucket( pvKey, uiKeyLen, &ui32CRC); - - // Search the bucket for an object with a matching - // key. - - pObject = m_ppHashTable[ uiBucket]; - while( pObject) - { - if( pObject->getKeyCRC() == ui32CRC) - { - void * pvTmpKey; - FLMUINT uiTmpKeyLen; - - pvTmpKey = pObject->getKey( &uiTmpKeyLen); - if( uiTmpKeyLen == uiKeyLen && - f_memcmp( pvTmpKey, pvKey, uiKeyLen) == 0) - { - break; - } - } - pObject = pObject->m_pNextInBucket; - } - - if( !pObject) - { - rc = RC_SET( FERR_NOT_FOUND); - goto Exit; - } - - *ppObject = pObject; - -Exit: - - return( rc); -} - -/**************************************************************************** -Desc: Adds an object to the hash table -****************************************************************************/ -RCODE F_HashTable::addObject( - F_HashObject * pObject) -{ - FLMUINT uiBucket; - F_HashObject * pTmp; - void * pvKey; - FLMUINT uiKeyLen; - FLMUINT32 ui32CRC; - FLMBOOL bMutexLocked = FALSE; - RCODE rc = FERR_OK; - - // Calculate and set the objects hash bucket - - flmAssert( pObject->getHashBucket() == F_INVALID_HASH_BUCKET); - - pvKey = pObject->getKey( &uiKeyLen); - flmAssert( uiKeyLen); - - uiBucket = getHashBucket( pvKey, uiKeyLen, &ui32CRC); - pObject->setKeyCRC( ui32CRC); - - // Lock the mutex - - if( m_hMutex != F_MUTEX_NULL) - { - f_mutexLock( m_hMutex); - bMutexLocked = TRUE; - } - - // Make sure the object doesn't already exist - - if( RC_BAD( rc = findObject( pvKey, uiKeyLen, &pTmp))) - { - if( rc != FERR_NOT_FOUND) - { - goto Exit; - } - rc = FERR_OK; - } - else - { - flmAssert( 0); - rc = RC_SET( FERR_EXISTS); - goto Exit; - } - - // Add a reference to the object - - pObject->AddRef(); - - // Link the object into the appropriate lists - - linkObject( pObject, uiBucket); - -Exit: - - // Unlock the mutex - - if( bMutexLocked) - { - f_mutexUnlock( m_hMutex); - } - - return( rc); -} - -/**************************************************************************** -Desc: Returns the next object in the linked list of objects in the hash - table. If *ppObject == NULL, the first object will be returned. -****************************************************************************/ -RCODE F_HashTable::getNextObjectInGlobal( - F_HashObject ** ppObject) -{ - FLMBOOL bMutexLocked = FALSE; - F_HashObject * pOldObj; - RCODE rc = FERR_OK; - - // Lock the mutex - - if( m_hMutex != F_MUTEX_NULL) - { - f_mutexLock( m_hMutex); - bMutexLocked = TRUE; - } - - if( !(*ppObject)) - { - *ppObject = m_pGlobalList; - } - else - { - pOldObj = *ppObject; - *ppObject = (*ppObject)->m_pNextInGlobal; - pOldObj->Release(); - } - - if( *ppObject == NULL) - { - rc = RC_SET( FERR_EOF_HIT); - goto Exit; - } - - (*ppObject)->AddRef(); - -Exit: - - if( bMutexLocked) - { - f_mutexUnlock( m_hMutex); - } - - return( rc); -} - -/**************************************************************************** -Desc: Retrieves an object from the hash table with the specified key -****************************************************************************/ -RCODE F_HashTable::getObject( - void * pvKey, - FLMUINT uiKeyLen, - F_HashObject ** ppObject, - FLMBOOL bRemove) -{ - F_HashObject * pObject; - FLMBOOL bMutexLocked = FALSE; - RCODE rc = FERR_OK; - - // Lock the mutex - - if( m_hMutex != F_MUTEX_NULL) - { - f_mutexLock( m_hMutex); - bMutexLocked = TRUE; - } - - // Search for an object with a matching key. - - if( RC_BAD( rc = findObject( pvKey, uiKeyLen, &pObject))) - { - goto Exit; - } - - if( pObject && bRemove) - { - unlinkObject( pObject); - if( !ppObject) - { - pObject->Release(); - pObject = NULL; - } - } - - if( ppObject) - { - if( !bRemove) - { - pObject->AddRef(); - } - *ppObject = pObject; - pObject = NULL; - } - -Exit: - - if( bMutexLocked) - { - f_mutexUnlock( m_hMutex); - } - - return( rc); -} - -/**************************************************************************** -Desc: Removes an object from the hash table by key -****************************************************************************/ -RCODE F_HashTable::removeObject( - void * pvKey, - FLMUINT uiKeyLen) -{ - return( getObject( pvKey, uiKeyLen, NULL, TRUE)); -} - -/**************************************************************************** -Desc: Removes an object from the hash table by object pointer -****************************************************************************/ -RCODE F_HashTable::removeObject( - F_HashObject * pObject) -{ - FLMUINT uiKeyLen; - void * pvKey; - - pvKey = pObject->getKey( &uiKeyLen); - return( getObject( pvKey, uiKeyLen, NULL, TRUE)); -} - -/**************************************************************************** -Desc: Calculates the hash bucket of a key and optionally returns the key's - CRC. -****************************************************************************/ -FLMUINT F_HashTable::getHashBucket( - void * pvKey, - FLMUINT uiLen, - FLMUINT32 * pui32KeyCRC) -{ - FLMUINT32 ui32CRC = 0; - - f_updateCRC( m_pCRCTable, (FLMBYTE *)pvKey, uiLen, &ui32CRC); - if( pui32KeyCRC) - { - *pui32KeyCRC = ui32CRC; - } - return( ui32CRC % m_uiBuckets); -} - -/**************************************************************************** -Desc: Links an object to the global list and also to its bucket -Notes: This routine assumes that the bucket's mutex is already locked - if the hash table is multi-threaded. -****************************************************************************/ -void F_HashTable::linkObject( - F_HashObject * pObject, - FLMUINT uiBucket) -{ - flmAssert( uiBucket < m_uiBuckets); - flmAssert( pObject->getHashBucket() == F_INVALID_HASH_BUCKET); - - // Set the object's bucket - - pObject->setHashBucket( uiBucket); - - // Link the object to its hash bucket - - pObject->m_pNextInBucket = m_ppHashTable[ uiBucket]; - if( m_ppHashTable[ uiBucket]) - { - m_ppHashTable[ uiBucket]->m_pPrevInBucket = pObject; - } - m_ppHashTable[ uiBucket] = pObject; - - // Link to the global list - - pObject->m_pNextInGlobal = m_pGlobalList; - if( m_pGlobalList) - { - m_pGlobalList->m_pPrevInGlobal = pObject; - } - m_pGlobalList = pObject; -} - -/**************************************************************************** -Desc: Unlinks an object from its bucket and the global list. -Notes: This routine assumes that the bucket's mutex is already locked - if the hash table is multi-threaded. -****************************************************************************/ -void F_HashTable::unlinkObject( - F_HashObject * pObject) -{ - FLMUINT uiBucket = pObject->getHashBucket(); - - // Is the bucket valid? - - flmAssert( uiBucket < m_uiBuckets); - - // Unlink from the hash bucket - - if( pObject->m_pNextInBucket) - { - pObject->m_pNextInBucket->m_pPrevInBucket = pObject->m_pPrevInBucket; - } - - if( pObject->m_pPrevInBucket) - { - pObject->m_pPrevInBucket->m_pNextInBucket = pObject->m_pNextInBucket; - } - else - { - m_ppHashTable[ uiBucket] = pObject->m_pNextInBucket; - } - - pObject->m_pPrevInBucket = NULL; - pObject->m_pNextInBucket = NULL; - pObject->setHashBucket( F_INVALID_HASH_BUCKET); - - // Unlink from the global list - - if( pObject->m_pNextInGlobal) - { - pObject->m_pNextInGlobal->m_pPrevInGlobal = pObject->m_pPrevInGlobal; - } - - if( pObject->m_pPrevInGlobal) - { - pObject->m_pPrevInGlobal->m_pNextInGlobal = pObject->m_pNextInGlobal; - } - else - { - m_pGlobalList = pObject->m_pNextInGlobal; - } - - pObject->m_pPrevInGlobal = NULL; - pObject->m_pNextInGlobal = NULL; -} - -/**************************************************************************** -Desc: Constructor -****************************************************************************/ -F_SessionDb::F_SessionDb() -{ - m_hDb = HFDB_NULL; - f_memset( m_ucKey, 0, sizeof( m_ucKey)); -} - -/**************************************************************************** -Desc: Destructor -****************************************************************************/ -F_SessionDb::~F_SessionDb() -{ - if( m_hDb != HFDB_NULL) - { - FlmDbClose( &m_hDb); - } -} - -/**************************************************************************** -Desc: Configures a database object prior to being used -****************************************************************************/ -RCODE F_SessionDb::setupSessionDb( - F_Session * pSession, - HFDB hDb) -{ - flmAssert( hDb != HFDB_NULL); - flmAssert( m_hDb == HFDB_NULL); - - m_pSession = pSession; - m_hDb = hDb; - - // Handle - - f_sprintf( (char *)&m_ucKey[ 0], "%0*X", - (int)(sizeof( FLMUINT) * 2), - (unsigned)((FLMUINT)hDb)); - - // Token - - f_sprintf( (char *)&m_ucKey[ sizeof( FLMUINT) * 2], "%0*X", - (int)(sizeof( FLMUINT) * 2), - (unsigned)m_pSession->getNextToken()); - - m_ucKey[ sizeof( m_ucKey) - 1] = 0; - return( FERR_OK); -} - -/**************************************************************************** -Desc: Returns the key and key length of a database object -****************************************************************************/ -void * F_SessionDb::getKey( - FLMUINT * puiKeyLen) -{ - if( puiKeyLen) - { - *puiKeyLen = (FLMUINT)sizeof( m_ucKey); - } - return( (void *)(&m_ucKey[ 0])); -} - -/**************************************************************************** -Desc: -****************************************************************************/ -FLMEXP RCODE FLMAPI FlmAllocFileSystem( - F_FileSystem ** ppFileSystem) -{ - RCODE rc = FERR_OK; - - flmAssert( ppFileSystem && *ppFileSystem == NULL); - - if( (*ppFileSystem = f_new F_FileSystemImp) == NULL) - { - rc = RC_SET( FERR_MEM); - goto Exit; - } - -Exit: - - return( rc); -} - -/**************************************************************************** -Desc: -****************************************************************************/ -FLMEXP RCODE FLMAPI FlmAllocDirHdl( - F_DirHdl ** ppDirHdl) -{ - RCODE rc = FERR_OK; - - flmAssert( ppDirHdl && *ppDirHdl == NULL); - - if( (*ppDirHdl = f_new F_DirHdlImp) == NULL) - { - rc = RC_SET( FERR_MEM); - goto Exit; - } - -Exit: - - return( rc); -} - -/**************************************************************************** -Desc: -****************************************************************************/ -FLMEXP RCODE FLMAPI FlmAllocFileHandle( - F_FileHdl ** ppFileHandle) -{ - RCODE rc = FERR_OK; - - flmAssert( ppFileHandle && *ppFileHandle == NULL); - - if( (*ppFileHandle = f_new F_FileHdlImp) == NULL) - { - rc = RC_SET( FERR_MEM); - goto Exit; - } - -Exit: - - return( rc); -} - -/**************************************************************************** -Desc: Deletes (releases) and F_CCS objected referenced in the ITT table. -*****************************************************************************/ -void flmDeleteCCSRefs( - FDICT * pDict - ) -{ - FLMUINT uiLoop; - F_CCS * pCcs = NULL; - ITT * pItt; - - if (pDict && pDict->pIttTbl) - { - for ( pItt = pDict->pIttTbl, uiLoop = 0; - uiLoop < pDict->uiIttCnt; pItt++, uiLoop++) - { - if (ITT_IS_ENCDEF(pItt)) - { - pCcs = (F_CCS *)pItt->pvItem; - pItt->pvItem = NULL; - if (pCcs) - { - pCcs->Release(); - pCcs = NULL; - } - } - } - } -} +//------------------------------------------------------------------------- +// Desc: Initialization and shutdown and system data. +// Tabs: 3 +// +// Copyright (c) 1995-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: fsysdata.cpp 12334 2006-01-23 12:45:35 -0700 (Mon, 23 Jan 2006) dsanders $ +//------------------------------------------------------------------------- + +#define ALLOCATE_SYS_DATA 1 + +#include "flaimsys.h" + +#ifdef FLM_AIX + #include +#endif +#ifdef FLM_HPUX + #include + #include + #include +#endif + +#if defined( FLM_WIN) || defined( FLM_NLM) + #define FLM_CAN_GET_PHYS_MEM +#elif defined( FLM_UNIX) + #if defined( _SC_AVPHYS_PAGES) || defined( FLM_HPUX) + #define FLM_CAN_GET_PHYS_MEM + #endif +#endif + +#define FLM_MIN_FREE_BYTES (2 * 1024 * 1024) + +#ifdef FLM_32BIT + #if defined( FLM_LINUX) + + // With mmap'd memory on Linux, you're effectively limited to about ~2 GB. + // Userspace only gets ~3GB of useable address space anyway, and then you + // have all of the thread stacks too, which you can't have + // overlapping the heap. + + #define FLM_MAX_CACHE_SIZE (1500 * 1024 * 1024) + #else + #define FLM_MAX_CACHE_SIZE (2000 * 1024 * 1024) + #endif +#else + #define FLM_MAX_CACHE_SIZE (~((FLMUINT)0)) +#endif + + +FLMINT32 gv_i32FlmSysSpinLock = 0; +FLMUINT gv_uiFlmSysStartupCount = 0; +FLMBOOL gv_bNetWareStartupCalled = FALSE; + +#ifdef FLM_NLM + extern "C" + { + void flmHttpConfig( + FLMBOOL bEnable, + const char * pszParams); + } +#endif + +FSTATIC void flmInitHashTbl( + FBUCKET * pHashTable, + FLMUINT uiHashEntries, + f_randomGenerator * RandGen); + +#ifdef FLM_CAN_GET_PHYS_MEM +FSTATIC FLMUINT flmGetCacheBytes( + FLMUINT uiPercent, + FLMUINT uiMin, + FLMUINT uiMax, + FLMUINT uiMinToLeave, + FLMBOOL bCalcOnAvailMem, + FLMUINT uiBytesCurrentlyInUse); +#endif + +FSTATIC void flmLockSysData( void); + +FSTATIC void flmUnlockSysData( void); + +FSTATIC RCODE flmSetCacheLimits( + FLMUINT uiNewTotalCacheSize, + FLMBOOL bPreallocateCache); + +FSTATIC void flmFreeEvent( + FEVENT * pEvent, + F_MUTEX hMutex, + FEVENT ** ppEventListRV); + +FSTATIC RCODE flmCloseDbFile( + const char * pszDbFileName, + const char * pszDataDir); + +FSTATIC void flmShutdownDbThreads( + FFILE * pFile); + +FSTATIC void flmCleanup( void); + +FSTATIC void flmUnlinkFileFromBucket( + FFILE * pFile); + +RCODE flmMonitor( + F_Thread * pThread); + +FSTATIC RCODE flmRegisterHttpCallback( + FLM_MODULE_HANDLE hModule, + const char * pszUrlString); + +FSTATIC RCODE flmDeregisterHttpCallback( void); + +/**************************************************************************** +Desc: This routine frees all of the notify requests in a list of requests. +****************************************************************************/ +FINLINE void flmFreeNotifyList( + FNOTIFY ** ppNotifyRV) +{ + FNOTIFY * pTmp; + FNOTIFY * pNotify = *ppNotifyRV; + + while (pNotify) + { + f_semDestroy( &pNotify->hSem); + pTmp = pNotify; + pNotify = pNotify->pNext; + (void)f_free( &pTmp); + } + + *ppNotifyRV = NULL; +} + +/**************************************************************************** +Desc: Sets the path for all temporary files that come into use within a + FLAIM share structure. The share mutex should be locked when + settting when called from FlmConfig(). +****************************************************************************/ +FINLINE RCODE flmSetTmpDir( + const char * pszTmpDir) +{ + RCODE rc; + + if( RC_BAD( rc = gv_FlmSysData.pFileSystem->Exists( pszTmpDir))) + { + goto Exit; + } + + f_strcpy( gv_FlmSysData.szTempDir, pszTmpDir); + gv_FlmSysData.bTempDirSet = TRUE; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: This routine frees all of the local dictionaries in a list of + local dictionaries. +****************************************************************************/ +FINLINE void flmFreeDictList( + FDICT ** ppDictRV) +{ + FDICT * pTmp; + FDICT * pDict = *ppDictRV; + + while (pDict) + { + pTmp = pDict; + pDict = pDict->pNext; + flmFreeDict( pTmp); + } + + *ppDictRV = NULL; +} + +/**************************************************************************** +Desc: This routine initializes a hash table. +****************************************************************************/ +FSTATIC void flmInitHashTbl( + FBUCKET * pHashTable, + FLMUINT uiHashEntries, + f_randomGenerator * RandGen) +{ + FLMUINT uiCnt; + FLMUINT uiRandVal; + FLMUINT uiTempVal; + + for( uiCnt = 0; uiCnt < uiHashEntries; uiCnt++) + { + pHashTable [uiCnt].uiHashValue = (FLMBYTE)uiCnt; + pHashTable [uiCnt].pFirstInBucket = NULL; + } + + if (uiHashEntries <= 256) + { + for (uiCnt = 0; uiCnt < uiHashEntries - 1; uiCnt++) + { + uiRandVal = (FLMBYTE) f_randomChoice( RandGen, (FLMINT32)uiCnt, + (FLMINT32)(uiHashEntries - 1)); + if (uiRandVal != uiCnt) + { + uiTempVal = (FLMBYTE)pHashTable [uiCnt].uiHashValue; + pHashTable [uiCnt].uiHashValue = pHashTable [uiRandVal].uiHashValue; + pHashTable [uiRandVal].uiHashValue = uiTempVal; + } + } + } +} + +/**************************************************************************** +Desc: This routine allocates and initializes a hash table. +****************************************************************************/ +RCODE flmAllocHashTbl( + FLMUINT uiHashTblSize, + FBUCKET ** ppHashTblRV) +{ + RCODE rc = FERR_OK; + f_randomGenerator RandGen; + FBUCKET * pHashTbl = NULL; + + // Allocate memory for the hash table + + if( RC_BAD( rc = f_calloc( + (FLMUINT)(sizeof( FBUCKET)) * uiHashTblSize, &pHashTbl))) + { + goto Exit; + } + + // Initialize the hash table + + f_randomSetSeed( &RandGen, 1); + flmInitHashTbl( pHashTbl, uiHashTblSize, &RandGen); + +Exit: + + *ppHashTblRV = pHashTbl; + return( rc); +} + +/**************************************************************************** +Desc: This routine determines the number of cache bytes to use for caching + based on a percentage of available physical memory or a percentage + of physical memory (depending on bCalcOnAvailMem flag). + uiBytesCurrentlyInUse indicates how many bytes are currently allocated + by FLAIM - so it can factor that in if the calculation is to be based + on the available memory. + Lower limit is 1 megabyte. +****************************************************************************/ +#ifdef FLM_CAN_GET_PHYS_MEM +FSTATIC FLMUINT flmGetCacheBytes( + FLMUINT uiPercent, + FLMUINT uiMin, + FLMUINT uiMax, + FLMUINT uiMinToLeave, + FLMBOOL bCalcOnAvailMem, + FLMUINT uiBytesCurrentlyInUse + ) +{ + FLMUINT uiMem; +#if defined( FLM_WIN) + MEMORYSTATUS MemStatus; +#elif defined( FLM_UNIX) + FLMUINT uiProcMemLimit = FLM_MAX_UINT; + FLMUINT uiProcVMemLimit = FLM_MAX_UINT; +#endif + +#if defined( FLM_WIN) + GlobalMemoryStatus( &MemStatus); + uiMem = (FLMUINT)((bCalcOnAvailMem) + ? (FLMUINT)MemStatus.dwAvailPhys + : (FLMUINT)MemStatus.dwTotalPhys); +#elif defined( FLM_UNIX) + { +#ifdef FLM_AIX + struct vminfo tmpvminfo; + #ifdef _SC_PAGESIZE + long iPageSize = sysconf(_SC_PAGESIZE); + #else + long iPageSize = 4096; + #endif + + if( iPageSize == -1) + { + // If sysconf returned an error, resort to using the default + // page size for the Power architecture. + + iPageSize = 4096; + } + + uiMem = FLM_MAX_UINT; + if( vmgetinfo( &tmpvminfo, VMINFO, sizeof( tmpvminfo)) != -1) + { + if( bCalcOnAvailMem) + { + if( tmpvminfo.numfrb < FLM_MAX_UINT) + { + uiMem = (FLMUINT)tmpvminfo.numfrb; + } + } + else + { + if( tmpvminfo.memsizepgs < FLM_MAX_UINT) + { + uiMem = (FLMUINT)tmpvminfo.memsizepgs; + } + } + } +#elif defined( FLM_HPUX) + long iPageSize; + struct pst_static pst; + + if (pstat_getstatic( &pst, sizeof( pst), (size_t)1, 0) == -1) + { + iPageSize = 4096; + } + else + { + iPageSize = pst.page_size; + } + if (bCalcOnAvailMem) + { + struct pst_dynamic dyn; + + if (pstat_getdynamic( &dyn, sizeof( dyn), 1, 0) != -1) + { + uiMem = (FLMUINT)dyn.psd_free; + } + else + { + uiMem = 0; + } + } + else + { + uiMem = (FLMUINT)pst.physical_memory; + } +#else + + long iPageSize = sysconf(_SC_PAGESIZE); + + // Get the amount of memory available to the system + + uiMem = (FLMUINT)((bCalcOnAvailMem) + ? (FLMUINT)sysconf(_SC_AVPHYS_PAGES) + : (FLMUINT)sysconf(_SC_PHYS_PAGES)); +#endif + + if (FLM_MAX_UINT / (FLMUINT)iPageSize >= uiMem) + { + uiMem *= (FLMUINT)iPageSize; + } + else + { + uiMem = FLM_MAX_UINT; + } + + #if defined( RLIMIT_VMEM) + // Bump the process soft virtual limit up to the hard limit + { + struct rlimit rlim; + + if( getrlimit( RLIMIT_VMEM, &rlim) == 0) + { + if( rlim.rlim_cur < rlim.rlim_max) + { + rlim.rlim_cur = rlim.rlim_max; + (void)setrlimit( RLIMIT_VMEM, &rlim); + if( getrlimit( RLIMIT_VMEM, &rlim) != 0) + { + rlim.rlim_cur = RLIM_INFINITY; + rlim.rlim_max = RLIM_INFINITY; + } + } + + if( rlim.rlim_cur != RLIM_INFINITY) + { + uiProcVMemLimit = (FLMUINT)rlim.rlim_cur; + } + } + } + #endif + + #if defined( RLIMIT_DATA) + + // Bump the process soft heap limit up to the hard limit + { + struct rlimit rlim; + + if( getrlimit( RLIMIT_DATA, &rlim) == 0) + { + if( rlim.rlim_cur < rlim.rlim_max) + { + rlim.rlim_cur = rlim.rlim_max; + (void)setrlimit( RLIMIT_DATA, &rlim); + if( getrlimit( RLIMIT_DATA, &rlim) != 0) + { + rlim.rlim_cur = RLIM_INFINITY; + rlim.rlim_max = RLIM_INFINITY; + } + } + + if( rlim.rlim_cur != RLIM_INFINITY) + { + uiProcMemLimit = (FLMUINT)rlim.rlim_cur; + } + } + } + + #endif + } +#elif defined( FLM_NLM) + { + FLMUINT uiCacheBufferSize = GetCacheBufferSize(); + + uiMem = (FLMUINT)((bCalcOnAvailMem) + ? (FLMUINT)GetCurrentNumberOfCacheBuffers() + : (FLMUINT)GetOriginalNumberOfCacheBuffers()); + + // Operating System will never give up last three hundred buffers. + + if (uiMem > 300) + { + uiMem -= 300; + } + else + { + uiMem = 0; + } + if (uiMem > FLM_MAX_UINT / uiCacheBufferSize) + { + uiMem = FLM_MAX_UINT; + } + else + { + uiMem *= uiCacheBufferSize; + } + + // Get available memory in local process pool + + if (bCalcOnAvailMem) + { + FLMUINT uiFreeBytes; + FLMUINT uiFreeNodes; + FLMUINT uiAllocatedBytes; + FLMUINT uiAllocatedNodes; + FLMUINT uiTotalMemory; + + if (GetNLMAllocMemoryCounts( f_getNLMHandle(), + &uiFreeBytes, &uiFreeNodes, + &uiAllocatedBytes, &uiAllocatedNodes, + &uiTotalMemory) == 0) + { + if (uiMem > FLM_MAX_UINT - uiFreeBytes) + { + uiMem = FLM_MAX_UINT; + } + else + { + uiMem += uiFreeBytes; + } + } + } + } +#else + #error Getting physical memory is not supported by this platform. +#endif + + // If we are basing the calculation on available physical memory, + // take in to account what has already been allocated. + + if (bCalcOnAvailMem) + { + if (uiMem > FLM_MAX_UINT - uiBytesCurrentlyInUse) + { + uiMem = FLM_MAX_UINT; + } + else + { + uiMem += uiBytesCurrentlyInUse; + } + } + + // Determine if there are limits on the amount of memory the + // process can access and reset uiMem accordingly. There may + // be more available memory than the process is able to access. + +#ifdef FLM_WIN + + // There could be more physical memory in the system than we could + // actually allocate in our virtual address space. Thus, we need to + // make sure that we never exceed our total virtual address space. + + if (uiMem > (FLMUINT)MemStatus.dwTotalVirtual) + { + uiMem = (FLMUINT)MemStatus.dwTotalVirtual; + } + +#elif defined( FLM_UNIX) + + // The process might be limited in the amount of memory it + // can access. + + if ( uiMem > uiProcMemLimit) + { + uiMem = uiProcMemLimit; + } + + if( uiMem > uiProcVMemLimit) + { + uiMem = uiProcVMemLimit; + } + +#endif + + // If uiMax is zero, use uiMinToLeave to calculate the maximum. + + if (!uiMax) + { + if (!uiMinToLeave) + { + uiMax = uiMem; + } + else if (uiMinToLeave < uiMem) + { + uiMax = uiMem - uiMinToLeave; + } + else + { + uiMax = 0; + } + } + + // Calculate memory as a percentage of memory. + + uiMem = (FLMUINT)((uiMem > FLM_MAX_UINT / 100) + ? (FLMUINT)(uiMem / 100) * uiPercent + : (FLMUINT)(uiMem * uiPercent) / 100); + + // Don't go above the maximum. + + if (uiMem > uiMax) + { + uiMem = uiMax; + } + + // Don't go below the minimum. + + if (uiMem < uiMin) + { + uiMem = uiMin; + } + return( uiMem); +} +#endif + +/*************************************************************************** +Desc: Lock the system data structure for access - called only by startup + and shutdown. NOTE: On platforms that do not support atomic exchange + this is less than perfect - won't handle tight race conditions. +***************************************************************************/ +FSTATIC void flmLockSysData( void) +{ + // Obtain the spin lock + + while (ftkAtomicExchange( &gv_i32FlmSysSpinLock, 1) == 1) + { + f_sleep( 10); + } +} + +/*************************************************************************** +Desc: Unlock the system data structure for access - called only by startup + and shutdown. +***************************************************************************/ +FSTATIC void flmUnlockSysData( void) +{ + (void)ftkAtomicExchange( &gv_i32FlmSysSpinLock, 0); +} + +/*API~*********************************************************************** +Desc : Startup FLAIM. +Notes: This routine may be called multiple times. However, if that is done + FlmShutdown() should be called for each time this is called + successfully. This routine does not handle race conditions on + platforms that do not support atomic increment. +*END************************************************************************/ +FLMEXP RCODE FLMAPI FlmStartup( void) +{ + RCODE rc = FERR_OK; + FLMINT iEventCategory; + FLMUINT uiCacheBytes; +#ifdef FLM_USE_NICI + int iHandle; +#endif + + // Before starting anything, make sure the atomic primitives return the + // correct values on this platform + +#ifdef FLM_DEBUG + { + FLMINT32 i32Val = 10772; + FLMINT32 i32Tmp; + + flmAssert( ftkAtomicIncrement( &i32Val) == 10773); + flmAssert( ftkAtomicDecrement( &i32Val) == 10772); + + i32Tmp = ftkAtomicExchange( &i32Val, 10777); + + flmAssert( i32Tmp == 10772); + flmAssert( i32Val == 10777); + } +#endif + + flmLockSysData(); + + // See if FLAIM has already been started. If so, + // we are done. + + if (++gv_uiFlmSysStartupCount > 1) + { + goto Exit; + } + +#ifdef FLM_NLM + gv_bNetWareStartupCalled = TRUE; + if( RC_BAD( rc = f_netwareStartup())) + { + goto Exit; + } +#endif + + // Sanity check -- make sure we are using the correct + // byte-swap macros for this platform + + flmAssert( FB2UD( "\x0A\x0B\x0C\x0D") == 0x0D0C0B0A); + flmAssert( FB2UW( "\x0A\x0B") == 0x0B0A); + + // The memset needs to be first. + + f_memset( &gv_FlmSysData, 0, sizeof( FLMSYSDATA)); + +#if defined( FLM_LINUX) + flmGetLinuxKernelVersion( &gv_FlmSysData.uiLinuxMajorVer, + &gv_FlmSysData.uiLinuxMinorVer, + &gv_FlmSysData.uiLinuxRevision); + gv_FlmSysData.uiMaxFileSize = flmGetLinuxMaxFileSize( sizeof( FLMUINT)); +#elif defined( FLM_AIX) + // Call set setrlimit to increase the max allowed file size. + // We don't have a good way to deal with any errors returned by + // setrlimit(), so we just hope that there aren't any... + struct rlimit rlim; + rlim.rlim_cur = RLIM_INFINITY; + rlim.rlim_max = RLIM_INFINITY; + setrlimit( RLIMIT_FSIZE, &rlim); + gv_FlmSysData.uiMaxFileSize = F_MAXIMUM_FILE_SIZE; +#else + gv_FlmSysData.uiMaxFileSize = F_MAXIMUM_FILE_SIZE; +#endif + + flmAssert( gv_FlmSysData.uiMaxFileSize); + + // Initialize memory tracking variables - should be done before + // call to f_memoryInit(). + +#ifdef FLM_DEBUG + + // Variables for memory allocation tracking. + + gv_FlmSysData.bTrackLeaks = TRUE; + gv_FlmSysData.hMemTrackingMutex = F_MUTEX_NULL; +#ifdef DEBUG_SIM_OUT_OF_MEM + f_randomSetSeed( &gv_FlmSysData.memSimRandomGen, 1); +#endif +#endif + + gv_FlmSysData.hShareMutex = F_MUTEX_NULL; + gv_FlmSysData.hFileHdlMutex = F_MUTEX_NULL; + gv_FlmSysData.uiMaxStratifyIterations = DEFAULT_MAX_STRATIFY_ITERATIONS; + gv_FlmSysData.uiMaxStratifyTime = DEFAULT_MAX_STRATIFY_TIME; + + // Initialize the event categories to have no mutex. + + for (iEventCategory = 0; + iEventCategory < F_MAX_EVENT_CATEGORIES; + iEventCategory++) + { + gv_FlmSysData.EventHdrs [iEventCategory].hMutex = F_MUTEX_NULL; + } + + // Memory initialization should be first. + + f_memoryInit(); + +#if defined( FLM_NLM) || (defined( FLM_WIN) && !defined( FLM_64BIT)) + /* Initialize the checksum code variable. */ + InitFastBlockCheckSum(); +#endif + +#if defined( FLM_NLM) + if (RC_BAD( rc = nssInitialize())) + { + goto Exit; + } +#endif + +#ifdef FLM_DBG_LOG + flmDbgLogInit(); +#endif + + /* Initialize all of the fields. */ + + FLM_SECS_TO_TIMER_UNITS( DEFAULT_MAX_UNUSED_TIME, + gv_FlmSysData.uiMaxUnusedTime); + FLM_SECS_TO_TIMER_UNITS( DEFAULT_MAX_CP_INTERVAL, + gv_FlmSysData.uiMaxCPInterval); + FLM_SECS_TO_TIMER_UNITS( DEFAULT_MAX_TRANS_SECS, + gv_FlmSysData.uiMaxTransTime); + FLM_SECS_TO_TIMER_UNITS( DEFAULT_MAX_TRANS_INACTIVE_SECS, + gv_FlmSysData.uiMaxTransInactiveTime); + +#ifdef FLM_CAN_GET_PHYS_MEM + gv_FlmSysData.bDynamicCacheAdjust = TRUE; + gv_FlmSysData.uiCacheAdjustPercent = DEFAULT_CACHE_ADJUST_PERCENT; + gv_FlmSysData.uiCacheAdjustMin = DEFAULT_CACHE_ADJUST_MIN; + gv_FlmSysData.uiCacheAdjustMax = DEFAULT_CACHE_ADJUST_MAX; + gv_FlmSysData.uiCacheAdjustMinToLeave = DEFAULT_CACHE_ADJUST_MIN_TO_LEAVE; + FLM_SECS_TO_TIMER_UNITS( DEFAULT_CACHE_ADJUST_INTERVAL, + gv_FlmSysData.uiCacheAdjustInterval); + uiCacheBytes = flmGetCacheBytes( gv_FlmSysData.uiCacheAdjustPercent, + gv_FlmSysData.uiCacheAdjustMin, + gv_FlmSysData.uiCacheAdjustMax, + gv_FlmSysData.uiCacheAdjustMinToLeave, TRUE, 0); +#else + gv_FlmSysData.bDynamicCacheAdjust = FALSE; + gv_FlmSysData.uiCacheAdjustInterval = 0; + uiCacheBytes = DEFAULT_CACHE_ADJUST_MIN; +#endif + + if( uiCacheBytes > FLM_MAX_CACHE_SIZE) + { + uiCacheBytes = FLM_MAX_CACHE_SIZE; + } + + gv_FlmSysData.uiBlockCachePercentage = DEFAULT_BLOCK_CACHE_PERCENTAGE; + + FLM_SECS_TO_TIMER_UNITS( DEFAULT_CACHE_CLEANUP_INTERVAL, + gv_FlmSysData.uiCacheCleanupInterval); + FLM_SECS_TO_TIMER_UNITS( DEFAULT_UNUSED_CLEANUP_INTERVAL, + gv_FlmSysData.uiUnusedCleanupInterval); + + // Initialize the thread manager + + if( (gv_FlmSysData.pThreadMgr = f_new F_ThreadMgr) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = gv_FlmSysData.pThreadMgr->setupThreadMgr())) + { + goto Exit; + } + + // Initialize the serial number generator + + if( RC_BAD( rc = f_initSerialNumberGenerator())) + { + goto Exit; + } + + // Initialize the slab manager + + if( (gv_FlmSysData.pSlabManager = f_new F_SlabManager) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( !gv_FlmSysData.bDynamicCacheAdjust) + { + if( RC_BAD( rc = gv_FlmSysData.pSlabManager->setup( uiCacheBytes))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = gv_FlmSysData.pSlabManager->setup( 0))) + { + goto Exit; + } + } + + // Divide cache bytes evenly between block and record cache. + + gv_FlmSysData.uiMaxCache = uiCacheBytes; + uiCacheBytes >>= 1; + if (RC_BAD( rc = ScaInit( uiCacheBytes))) + { + goto Exit; + } + + if (RC_BAD( rc = flmRcaInit( uiCacheBytes))) + { + goto Exit; + } + + /* Create the mutex for controlling access to the structure. */ + + if (RC_BAD( rc = f_mutexCreate( &gv_FlmSysData.hShareMutex))) + { + goto Exit; + } + + if (RC_BAD( rc = f_mutexCreate( &gv_FlmSysData.hQueryMutex))) + { + goto Exit; + } + + if (RC_BAD(rc = f_mutexCreate( &gv_FlmSysData.hFileHdlMutex))) + { + goto Exit; + } + + if (RC_BAD(rc = f_mutexCreate( &gv_FlmSysData.hServerLockMgrMutex))) + { + goto Exit; + } + + if (RC_BAD( rc = f_mutexCreate( &gv_FlmSysData.HttpConfigParms.hMutex))) + { + goto Exit; + } + + if (RC_BAD( rc = f_mutexCreate( &gv_FlmSysData.hHttpSessionMutex))) + { + goto Exit; + } + + /* Initialize a statistics structure. */ + + if (RC_BAD( rc = flmStatInit( &gv_FlmSysData.Stats, TRUE))) + { + goto Exit; + } + gv_FlmSysData.bStatsInitialized = TRUE; + + /* Allocate memory for the file name hash table. */ + + if (RC_BAD(rc = flmAllocHashTbl( FILE_HASH_ENTRIES, + &gv_FlmSysData.pFileHashTbl))) + { + goto Exit; + } + + gv_FlmSysData.uiNextFFileId = 1; + + /* Allocate and Initialize FLAIM Shared File Handle Manager */ + + if ((gv_FlmSysData.pFileHdlMgr = + new F_FileHdlMgr( &gv_FlmSysData.hFileHdlMutex)) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if (RC_BAD( rc = gv_FlmSysData.pFileHdlMgr->Setup( DEFAULT_OPEN_THRESHOLD, + DEFAULT_MAX_UNUSED_TIME))) + { + goto Exit; + } + + /* Allocate and Initialize FLAIM Shared File System object */ + + if ((gv_FlmSysData.pFileSystem = f_new F_FileSystemImp) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + +#if defined( FLM_WIN) + OSVERSIONINFO versionInfo; + + versionInfo.dwOSVersionInfoSize = sizeof( OSVERSIONINFO); + if( !GetVersionEx( &versionInfo)) + { + return( MapWinErrorToFlaim( GetLastError(), FERR_FAILURE)); + } + + // Async writes are not supported on Win32s (3.1) or + // Win95, 98, ME, etc. + + gv_FlmSysData.bOkToDoAsyncWrites = + (versionInfo.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS && + versionInfo.dwPlatformId != VER_PLATFORM_WIN32s) + ? TRUE + : FALSE; +#else + gv_FlmSysData.bOkToDoAsyncWrites = TRUE; +#endif + + /* Allocate and Initialize FLAIM Server Lock Manager. */ + + if ((gv_FlmSysData.pServerLockMgr = + new ServerLockManager( &gv_FlmSysData.hServerLockMgrMutex)) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + // Set up the session manager + + if( (gv_FlmSysData.pSessionMgr = f_new F_SessionMgr) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = gv_FlmSysData.pSessionMgr->setupSessionMgr())) + { + goto Exit; + } + + // Set up hash table for lock manager. + + if (RC_BAD( rc = gv_FlmSysData.pServerLockMgr->SetupHashTbl())) + { + goto Exit; + } + + // Set up mutexes for the event table. + + for (iEventCategory = 0; + iEventCategory < F_MAX_EVENT_CATEGORIES; + iEventCategory++) + { + if (RC_BAD( rc = f_mutexCreate( + &gv_FlmSysData.EventHdrs [iEventCategory].hMutex))) + { + goto Exit; + } + } + + // Start the monitor thread - ALWAYS DO LAST. EVERYTHING MUST BE + // SETUP PROPERLY BEFORE STARTING THIS THREAD. + + if (RC_BAD( rc = f_threadCreate( &gv_FlmSysData.pMonitorThrd, + flmMonitor, "DB Monitor"))) + { + goto Exit; + } + +#ifdef FLM_USE_NICI + iHandle = f_getpid(); + + // Initialize NICI + if (CCS_Init(&iHandle)) + { + // Failure. + rc = RC_SET( FERR_NICI_INIT_FAILED); + goto Exit; + } +#endif + +Exit: + + // If not successful, free up any resources that were allocated. + + if (RC_BAD( rc)) + { + flmCleanup(); + } + flmUnlockSysData(); + return( rc); +} + +/**************************************************************************** +Desc: This routine sets the limits for record cache and block cache - dividing + the total cache between the two caches. It uses the same ratio + currently in force. +****************************************************************************/ +FSTATIC RCODE flmSetCacheLimits( + FLMUINT uiNewTotalCacheSize, + FLMBOOL bPreallocateCache) +{ + RCODE rc = FERR_OK; + FLMUINT uiNewBlockCacheSize; + FLMBOOL bResizeAfterConfig = FALSE; + + if( uiNewTotalCacheSize > FLM_MAX_CACHE_SIZE) + { + uiNewTotalCacheSize = FLM_MAX_CACHE_SIZE; + } + + if( gv_FlmSysData.bDynamicCacheAdjust || !bPreallocateCache) + { +DONT_PREALLOCATE: + + if( uiNewTotalCacheSize < gv_FlmSysData.uiMaxCache) + { + bResizeAfterConfig = TRUE; + } + else + { + if( RC_BAD( rc = gv_FlmSysData.pSlabManager->resize( 0))) + { + goto Exit; + } + } + + gv_FlmSysData.bCachePreallocated = FALSE; + } + else + { + if( RC_BAD( rc = gv_FlmSysData.pSlabManager->resize( + uiNewTotalCacheSize, &uiNewTotalCacheSize))) + { + + // Log a message indicating that we couldn't pre-allocate + // the cache + + flmLogMessage( + FLM_DEBUG_MESSAGE, + FLM_YELLOW, + FLM_BLACK, + "WARNING: Couldn't pre-allocate cache."); + + goto DONT_PREALLOCATE; + } + + gv_FlmSysData.bCachePreallocated = TRUE; + } + + if( gv_FlmSysData.uiBlockCachePercentage == 100) + { + uiNewBlockCacheSize = uiNewTotalCacheSize; + } + else + { + uiNewBlockCacheSize = (FLMUINT)((uiNewTotalCacheSize / 100) * + gv_FlmSysData.uiBlockCachePercentage); + } + + if (RC_OK( rc = ScaConfig( FLM_CACHE_LIMIT, + (void *)uiNewBlockCacheSize, (void *)0))) + { + rc = flmRcaConfig( FLM_CACHE_LIMIT, + (void *)(uiNewTotalCacheSize - uiNewBlockCacheSize), + (void *)0); + } + + if( bResizeAfterConfig) + { + (void)gv_FlmSysData.pSlabManager->resize( 0); + } + + gv_FlmSysData.uiMaxCache = uiNewTotalCacheSize; + +Exit: + + return( rc); +} + +/*API~*********************************************************************** +Desc : Configures how memory will be dynamically regulated. +*END************************************************************************/ +FLMEXP RCODE FLMAPI FlmSetDynamicMemoryLimit( + FLMUINT uiCacheAdjustPercent, + FLMUINT uiCacheAdjustMin, + FLMUINT uiCacheAdjustMax, + FLMUINT uiCacheAdjustMinToLeave + ) +{ +#ifndef FLM_CAN_GET_PHYS_MEM + F_UNREFERENCED_PARM( uiCacheAdjustPercent); + F_UNREFERENCED_PARM( uiCacheAdjustMin); + F_UNREFERENCED_PARM( uiCacheAdjustMax); + F_UNREFERENCED_PARM( uiCacheAdjustMinToLeave); + return( RC_SET( FERR_NOT_IMPLEMENTED)); +#else + RCODE rc = FERR_OK; + FLMUINT uiCacheBytes; + + f_mutexLock( gv_FlmSysData.hShareMutex); + f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); + gv_FlmSysData.bDynamicCacheAdjust = TRUE; + flmAssert( uiCacheAdjustPercent > 0 && + uiCacheAdjustPercent <= 100); + gv_FlmSysData.uiCacheAdjustPercent = uiCacheAdjustPercent; + gv_FlmSysData.uiCacheAdjustMin = uiCacheAdjustMin; + gv_FlmSysData.uiCacheAdjustMax = uiCacheAdjustMax; + gv_FlmSysData.uiCacheAdjustMinToLeave = uiCacheAdjustMinToLeave; + uiCacheBytes = flmGetCacheBytes( gv_FlmSysData.uiCacheAdjustPercent, + gv_FlmSysData.uiCacheAdjustMin, + gv_FlmSysData.uiCacheAdjustMax, + gv_FlmSysData.uiCacheAdjustMinToLeave, TRUE, + gv_FlmSysData.SCacheMgr.Usage.uiTotalBytesAllocated + + gv_FlmSysData.RCacheMgr.pRCacheAlloc->getTotalBytesAllocated()); + rc = flmSetCacheLimits( uiCacheBytes, FALSE); + f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); + f_mutexUnlock( gv_FlmSysData.hShareMutex); + return( rc); +#endif +} + +/*API~*********************************************************************** +Desc : Sets a hard memory limit for cache. +*END************************************************************************/ +FLMEXP RCODE FLMAPI FlmSetHardMemoryLimit( + FLMUINT uiPercent, + FLMBOOL bPercentOfAvail, + FLMUINT uiMin, + FLMUINT uiMax, + FLMUINT uiMinToLeave, + FLMBOOL bPreallocate + ) +{ + RCODE rc = FERR_OK; + + f_mutexLock( gv_FlmSysData.hShareMutex); + f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); + gv_FlmSysData.bDynamicCacheAdjust = FALSE; + if (uiPercent) + { +#ifndef FLM_CAN_GET_PHYS_MEM + F_UNREFERENCED_PARM( bPercentOfAvail); + F_UNREFERENCED_PARM( uiMin); + F_UNREFERENCED_PARM( uiMinToLeave); + rc = RC_SET( FERR_NOT_IMPLEMENTED); +#else + FLMUINT uiCacheBytes; + + uiCacheBytes = flmGetCacheBytes( uiPercent, uiMin, uiMax, uiMinToLeave, + bPercentOfAvail, + gv_FlmSysData.SCacheMgr.Usage.uiTotalBytesAllocated + + gv_FlmSysData.RCacheMgr.pRCacheAlloc->getTotalBytesAllocated()); + rc = flmSetCacheLimits( uiCacheBytes, bPreallocate); +#endif + } + else + { + rc = flmSetCacheLimits( uiMax, bPreallocate); + } + f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); + f_mutexUnlock( gv_FlmSysData.hShareMutex); + return( rc); +} + +/*API~*********************************************************************** +Desc : Returns information about memory usage. +*END************************************************************************/ +FLMEXP void FLMAPI FlmGetMemoryInfo( + FLM_MEM_INFO * pMemInfo) +{ + f_memset( pMemInfo, 0, sizeof( FLM_MEM_INFO)); + f_mutexLock( gv_FlmSysData.hShareMutex); + f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); + pMemInfo->bDynamicCacheAdjust = gv_FlmSysData.bDynamicCacheAdjust; + pMemInfo->uiCacheAdjustPercent = gv_FlmSysData.uiCacheAdjustPercent; + pMemInfo->uiCacheAdjustMin = gv_FlmSysData.uiCacheAdjustMin; + pMemInfo->uiCacheAdjustMax = gv_FlmSysData.uiCacheAdjustMax; + pMemInfo->uiCacheAdjustMinToLeave = gv_FlmSysData.uiCacheAdjustMinToLeave; + + // Return record cache information. + + f_memcpy( &pMemInfo->RecordCache, &gv_FlmSysData.RCacheMgr.Usage, + sizeof( FLM_CACHE_USAGE)); + + // Return block cache information. + + f_memcpy( &pMemInfo->BlockCache, &gv_FlmSysData.SCacheMgr.Usage, + sizeof( FLM_CACHE_USAGE)); + + pMemInfo->uiFreeBytes = gv_FlmSysData.SCacheMgr.uiFreeBytes; + pMemInfo->uiFreeCount = gv_FlmSysData.SCacheMgr.uiFreeCount; + pMemInfo->uiReplaceableCount = gv_FlmSysData.SCacheMgr.uiReplaceableCount; + pMemInfo->uiReplaceableBytes = gv_FlmSysData.SCacheMgr.uiReplaceableBytes; + + if( gv_FlmSysData.pFileHashTbl) + { + FLMUINT uiLoop; + FFILE * pFile; + + for( uiLoop = 0; uiLoop < FILE_HASH_ENTRIES; uiLoop++) + { + if( (pFile = (FFILE *)gv_FlmSysData.pFileHashTbl[ + uiLoop].pFirstInBucket) != NULL) + { + while( pFile) + { + if( pFile->pECacheMgr) + { + pFile->pECacheMgr->getStats( &pMemInfo->ECache, TRUE); + } + + if( pFile->uiDirtyCacheCount) + { + pMemInfo->uiDirtyBytes += + pFile->uiDirtyCacheCount * pFile->FileHdr.uiBlockSize; + pMemInfo->uiDirtyCount += pFile->uiDirtyCacheCount; + } + + if( pFile->uiNewCount) + { + pMemInfo->uiNewBytes += + pFile->uiNewCount * pFile->FileHdr.uiBlockSize; + pMemInfo->uiNewCount += pFile->uiNewCount; + } + + if( pFile->uiLogCacheCount) + { + pMemInfo->uiLogBytes += + pFile->uiLogCacheCount * pFile->FileHdr.uiBlockSize; + pMemInfo->uiLogCount += pFile->uiLogCacheCount; + } + + pFile = pFile->pNext; + } + } + } + } + + f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); + f_mutexUnlock( gv_FlmSysData.hShareMutex); +} + +/**************************************************************************** +Desc : Close a database file - all background threads are closed too. +****************************************************************************/ +FSTATIC RCODE flmCloseDbFile( + const char * pszDbFileName, + const char * pszDataDir) +{ + RCODE rc = FERR_OK; + FLMBOOL bMutexLocked = FALSE; + FFILE * pFile; + + f_mutexLock( gv_FlmSysData.hShareMutex); + bMutexLocked = TRUE; + +Retry: + + // Look up the file using flmFindFile to see if we have the + // file open. May unlock and re-lock the global mutex. + + if (RC_BAD( rc = flmFindFile( pszDbFileName, pszDataDir, &pFile))) + { + goto Exit; + } + + // If we did not find the file, we are OK. + + if (!pFile) + { + goto Exit; + } + + // If the file is in the process of being opened by another + // thread, wait for the open to complete. + + if (pFile->uiFlags & DBF_BEING_OPENED) + { + if (RC_BAD( rc = flmWaitNotifyReq( gv_FlmSysData.hShareMutex, + &pFile->pOpenNotifies, + (void *)0))) + { + // GW Bug #24307. If flmWaitNotifyReq returns a bad RC, assume that + // the other thread will unlock and free the pFile structure. This + // routine should only unlock the pFile if an error occurs at some + // other point. See flmVerifyFileUse. + + // *ppFileRV is set to NULL at Exit. + goto Exit; + } + + goto Retry; + } + + if( pFile->uiUseCount) + { + // Increment the use count temporarily so that the FFILE won't be + // moved to the NU list because of db close calls made by + // releaseFileResources. + + pFile->uiUseCount++; + + // Must unlock the mutex prior to calling releaseFileResources because + // it may call API-level routines (such as FlmDbClose) that do not + // expect the mutex to be locked. + + f_mutexUnlock( gv_FlmSysData.hShareMutex); + bMutexLocked = FALSE; + + // First, all session resources are released that are + // associated with the specified database + + if( gv_FlmSysData.pSessionMgr) + { + gv_FlmSysData.pSessionMgr->releaseFileResources( pFile); + } + + f_mutexLock( gv_FlmSysData.hShareMutex); + bMutexLocked = TRUE; + + // Decrement the temporary use count that was added above. + // By now, the FFILE may no longer be in use. If so, we need to + // link it to the NU list. + + if (!(--pFile->uiUseCount)) + { + // If the "must close" flag is set, it indicates that + // the FFILE is being forced to close. Put the FFILE in + // the NU list, but specify that it should be quickly + // timed-out. We link the file to the NU list even + // though we are going to unlink it below because + // flmLinkFileToNUList closes the RFL file(s). + + flmLinkFileToNUList( pFile, pFile->bMustClose); + } + } + + // If the FFILE is in the NU list, unlink it + + flmUnlinkFileFromNUList( pFile); + + // If we have non-background threads accessing the database, + // we cannot close the file. + + if (pFile->uiUseCount > pFile->uiInternalUseCount) + { + rc = RC_SET( FERR_TRANS_ACTIVE); + goto Exit; + } + + // Close the file. + + flmFreeFile( pFile); + + // Unlock the mutex + + f_mutexUnlock( gv_FlmSysData.hShareMutex); + bMutexLocked = FALSE; + + // Clean up any unused file handles + + (void)gv_FlmSysData.pFileHdlMgr->CheckAgedItems( (FLMUINT)0); + +Exit: + + if (bMutexLocked) + { + f_mutexUnlock( gv_FlmSysData.hShareMutex); + } + return( rc); +} + +/**************************************************************************** +Desc : Register our http callback function +****************************************************************************/ +FSTATIC RCODE flmRegisterHttpCallback( + FLM_MODULE_HANDLE hModule, + const char * pszUrlString) +{ + RCODE rc = FERR_OK; + char * pszTemp; + FLMUINT uiRegFlags; + + if (gv_FlmSysData.HttpConfigParms.bRegistered) + { + rc = RC_SET( FERR_HTTP_REGISTER_FAILURE); + goto Exit; + } + + // Need to save the Url string for later use... + + if( RC_BAD( rc = f_alloc( f_strlen( pszUrlString) + 1, &pszTemp))) + { + goto Exit; + } + + f_strcpy( pszTemp, pszUrlString); + + // Set the flags that tell the server what kind of authentication + // we want: + // HR_STK_BOTH = Allow both http and https + // HR_AUTH_USERSA = Allow any user in the NDS tree or the SAdmin user + // HR_REALM_NDS = Use the NDS realm for authentication + + uiRegFlags = HR_STK_BOTH | HR_AUTH_USERSA | HR_REALM_NDS; + + if (gv_FlmSysData.HttpConfigParms.fnReg) + { + if( gv_FlmSysData.HttpConfigParms.fnReg( hModule, pszUrlString, + (FLMUINT32)uiRegFlags, flmHttpCallback, NULL, NULL) != 0) + { + rc = RC_SET( FERR_HTTP_REGISTER_FAILURE); + goto Exit; + } + } + else + { + flmAssert( 0); + rc = RC_SET( FERR_NO_HTTP_STACK); + goto Exit; + } + + // Save the URL string in gv_FlmSysData + + gv_FlmSysData.HttpConfigParms.pszURLString = pszTemp; + gv_FlmSysData.HttpConfigParms.uiURLStringLen = f_strlen( pszTemp); + pszTemp = NULL; + + gv_FlmSysData.HttpConfigParms.bRegistered = TRUE; + +Exit: + + if (RC_BAD( rc)) + { + if (pszTemp) + { + f_free( &pszTemp); + pszTemp = NULL; + } + } + + return( rc); +} + +/**************************************************************************** +Desc : Deregister our http callback function +****************************************************************************/ +FSTATIC RCODE flmDeregisterHttpCallback() +{ + RCODE rc = FERR_OK; + if (!gv_FlmSysData.HttpConfigParms.bRegistered) + { + rc = RC_SET( FERR_HTTP_DEREG_FAILURE); + goto Exit; + } + + if ( !gv_FlmSysData.HttpConfigParms.fnDereg || + !gv_FlmSysData.HttpConfigParms.pszURLString ) + { + flmAssert( 0); + rc = RC_SET( FERR_NO_HTTP_STACK); + goto Exit; + } + + if ( gv_FlmSysData.HttpConfigParms.fnDereg( + gv_FlmSysData.HttpConfigParms.pszURLString, + flmHttpCallback) != 0) + { + rc = RC_SET( FERR_HTTP_DEREG_FAILURE); + goto Exit; + } + + if( gv_FlmSysData.HttpConfigParms.pszURLString) + { + f_free( &gv_FlmSysData.HttpConfigParms.pszURLString); + gv_FlmSysData.HttpConfigParms.pszURLString = NULL; + } + + // Now, tell the callback function to delete the webpage factory and cleanup + // the allocated memory etc... + f_mutexLock( gv_FlmSysData.HttpConfigParms.hMutex); + while (gv_FlmSysData.HttpConfigParms.uiUseCount) + { + f_mutexUnlock( gv_FlmSysData.HttpConfigParms.hMutex); + f_sleep( 10); + f_mutexLock( gv_FlmSysData.HttpConfigParms.hMutex); + } + flmHttpCallback( NULL, NULL); + f_mutexUnlock( gv_FlmSysData.HttpConfigParms.hMutex); + + + gv_FlmSysData.HttpConfigParms.bRegistered = FALSE; +Exit: + return( rc); +} + + +/*API~*********************************************************************** +Desc: Configures share attributes. +*END************************************************************************/ +FLMEXP RCODE FLMAPI FlmConfig( + eFlmConfigTypes eConfigType, + void * Value1, + void * Value2) +{ + RCODE rc = FERR_OK; + FLMUINT uiValue; + FLMUINT uiSave; + FLMUINT uiCurrTime; + FLMUINT uiSaveMax; + + switch( eConfigType) + { + + case FLM_OPEN_THRESHOLD: + uiValue = (FLMUINT) Value1; + rc = gv_FlmSysData.pFileHdlMgr->SetOpenThreshold( uiValue); + break; + + case FLM_CACHE_LIMIT: + { + f_mutexLock( gv_FlmSysData.hShareMutex); + f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); + gv_FlmSysData.bDynamicCacheAdjust = FALSE; + rc = flmSetCacheLimits( (FLMUINT)Value1, (FLMBOOL)Value2); + f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); + f_mutexUnlock( gv_FlmSysData.hShareMutex); + break; + } + + case FLM_BLOCK_CACHE_PERCENTAGE: + { + f_mutexLock( gv_FlmSysData.hShareMutex); + f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); + + if( (gv_FlmSysData.uiBlockCachePercentage = (FLMUINT)Value1) > 100) + { + gv_FlmSysData.uiBlockCachePercentage = 100; + } + + rc = flmSetCacheLimits( + gv_FlmSysData.SCacheMgr.Usage.uiMaxBytes + + gv_FlmSysData.RCacheMgr.Usage.uiMaxBytes, + gv_FlmSysData.bCachePreallocated); + + f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); + f_mutexUnlock( gv_FlmSysData.hShareMutex); + break; + } + + case FLM_SCACHE_DEBUG: + f_mutexLock( gv_FlmSysData.hShareMutex); + f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); + if (RC_OK( rc = ScaConfig( FLM_SCACHE_DEBUG, Value1, Value2))) + { + rc = flmRcaConfig( FLM_SCACHE_DEBUG, Value1, Value2); + } + f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); + f_mutexUnlock( gv_FlmSysData.hShareMutex); + break; + + case FLM_CLOSE_UNUSED_FILES: + + // Timeout inactive sessions + + if( gv_FlmSysData.pSessionMgr) + { + gv_FlmSysData.pSessionMgr->timeoutInactiveSessions( + (FLMUINT)Value1, TRUE); + } + + // Convert seconds to timer units + + FLM_SECS_TO_TIMER_UNITS( (FLMUINT) Value1, uiValue); + rc = gv_FlmSysData.pFileHdlMgr->CheckAgedItems( uiValue); + + // Free any other unused structures that have not been used for the + // specified amount of time. + + uiCurrTime = (FLMUINT)FLM_GET_TIMER(); + f_mutexLock( gv_FlmSysData.hShareMutex); + + // Temporarily set the maximum unused seconds in the FLMSYSDATA structure + // to the value that was passed in to Value1. Restore it after + // calling flmCheckNUStructs. + + uiSave = gv_FlmSysData.uiMaxUnusedTime; + gv_FlmSysData.uiMaxUnusedTime = uiValue; + + // May unlock and re-lock the global mutex. + flmCheckNUStructs( uiCurrTime); + gv_FlmSysData.uiMaxUnusedTime = uiSave; + f_mutexUnlock( gv_FlmSysData.hShareMutex); + break; + + case FLM_CLOSE_ALL_FILES: + rc = flmCloseAllFiles(); + break; + + case FLM_START_STATS: + (void)flmStatStart( &gv_FlmSysData.Stats); + + // Start query statistics, if they have not + // already been started. + + f_mutexLock( gv_FlmSysData.hQueryMutex); + if (!gv_FlmSysData.uiMaxQueries) + { + gv_FlmSysData.uiMaxQueries = 20; + gv_FlmSysData.bNeedToUnsetMaxQueries = TRUE; + } + f_mutexUnlock( gv_FlmSysData.hQueryMutex); + break; + + case FLM_STOP_STATS: + (void)flmStatStop( &gv_FlmSysData.Stats); + + // Stop query statistics, if they were + // started by a call to FLM_START_STATS. + + f_mutexLock( gv_FlmSysData.hQueryMutex); + if (gv_FlmSysData.bNeedToUnsetMaxQueries) + { + gv_FlmSysData.uiMaxQueries = 0; + flmFreeSavedQueries( TRUE); + // NOTE: flmFreeSavedQueries unlocks the mutex. + } + else + { + f_mutexUnlock( gv_FlmSysData.hQueryMutex); + } + break; + + case FLM_RESET_STATS: + + // Lock the record cache manager's mutex + + f_mutexLock( gv_FlmSysData.hShareMutex); + f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); + + // Reset the record cache statistics + + gv_FlmSysData.RCacheMgr.uiIoWaits = 0; + gv_FlmSysData.RCacheMgr.Usage.uiCacheHits = 0; + gv_FlmSysData.RCacheMgr.Usage.uiCacheHitLooks = 0; + gv_FlmSysData.RCacheMgr.Usage.uiCacheFaults = 0; + gv_FlmSysData.RCacheMgr.Usage.uiCacheFaultLooks = 0; + + // Reset the block cache statistics. + + gv_FlmSysData.SCacheMgr.uiIoWaits = 0; + gv_FlmSysData.SCacheMgr.Usage.uiCacheHits = 0; + gv_FlmSysData.SCacheMgr.Usage.uiCacheHitLooks = 0; + gv_FlmSysData.SCacheMgr.Usage.uiCacheFaults = 0; + gv_FlmSysData.SCacheMgr.Usage.uiCacheFaultLooks = 0; + + // Unlock the cache manager's mutex + + f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); + f_mutexUnlock( gv_FlmSysData.hShareMutex); + + (void)flmStatReset( &gv_FlmSysData.Stats, FALSE, TRUE); + + f_mutexLock( gv_FlmSysData.hQueryMutex); + uiSaveMax = gv_FlmSysData.uiMaxQueries; + gv_FlmSysData.uiMaxQueries = 0; + flmFreeSavedQueries( TRUE); + // NOTE: flmFreeSavedQueries unlocks the mutex. + + // Restore the old maximum + + if (uiSaveMax) + { + + // flmFreeSavedQueries unlocks the mutex, so we + // must relock it to restore the old maximum. + + f_mutexLock( gv_FlmSysData.hQueryMutex); + gv_FlmSysData.uiMaxQueries = uiSaveMax; + f_mutexUnlock( gv_FlmSysData.hQueryMutex); + } + break; + + case FLM_TMPDIR: + f_mutexLock( gv_FlmSysData.hShareMutex); + rc = flmSetTmpDir( (const char *)Value1); + f_mutexUnlock( gv_FlmSysData.hShareMutex); + break; + + case FLM_MAX_CP_INTERVAL: + FLM_SECS_TO_TIMER_UNITS( (FLMUINT) Value1, gv_FlmSysData.uiMaxCPInterval); + break; + + case FLM_BLOB_EXT: + { + const char * pszTmp = (const char *)Value1; + + if (!pszTmp) + { + gv_FlmSysData.ucBlobExt [0] = 0; + } + else + { + int iCnt; + + // Don't save any more than 63 characters. + + for (iCnt = 0; + ((iCnt < 63) && (*pszTmp)); + iCnt++, pszTmp++) + { + gv_FlmSysData.ucBlobExt [iCnt] = *pszTmp; + } + gv_FlmSysData.ucBlobExt [iCnt] = 0; + } + } + break; + + case FLM_MAX_TRANS_SECS: + FLM_SECS_TO_TIMER_UNITS( (FLMUINT) Value1, + gv_FlmSysData.uiMaxTransTime); + break; + + case FLM_MAX_TRANS_INACTIVE_SECS: + FLM_SECS_TO_TIMER_UNITS( (FLMUINT) Value1, + gv_FlmSysData.uiMaxTransInactiveTime); + break; + + case FLM_CACHE_ADJUST_INTERVAL: + FLM_SECS_TO_TIMER_UNITS( (FLMUINT)Value1, + gv_FlmSysData.uiCacheAdjustInterval); + break; + + case FLM_CACHE_CLEANUP_INTERVAL: + FLM_SECS_TO_TIMER_UNITS( (FLMUINT)Value1, + gv_FlmSysData.uiCacheCleanupInterval); + break; + + case FLM_UNUSED_CLEANUP_INTERVAL: + FLM_SECS_TO_TIMER_UNITS( (FLMUINT)Value1, + gv_FlmSysData.uiUnusedCleanupInterval); + break; + + case FLM_MAX_UNUSED_TIME: + f_mutexLock( gv_FlmSysData.hShareMutex); + FLM_SECS_TO_TIMER_UNITS( (FLMUINT)Value1, + gv_FlmSysData.uiMaxUnusedTime); + f_mutexUnlock( gv_FlmSysData.hShareMutex); + break; + + case FLM_OUT_OF_MEM_SIMULATION: +#ifdef DEBUG_SIM_OUT_OF_MEM + gv_FlmSysData.uiOutOfMemSimEnabledFlag = + (FLMUINT)((Value1) ? OUT_OF_MEM_SIM_ENABLED_FLAG : 0); +#else + rc = RC_SET( FERR_NOT_IMPLEMENTED); +#endif + break; + + case FLM_CACHE_CHECK: + gv_FlmSysData.bCheckCache = (FLMBOOL)((Value1 != 0) + ? (FLMBOOL)TRUE + : (FLMBOOL)FALSE); + break; + + case FLM_CLOSE_FILE: + rc = flmCloseDbFile( (const char *)Value1, (const char *)Value2); + break; + + case FLM_LOGGER: + f_mutexLock( gv_FlmSysData.hShareMutex); + if( !gv_FlmSysData.pLogger && Value1) + { + gv_FlmSysData.pLogger = (F_Logger *)Value1; + gv_FlmSysData.pLogger->lockLogger(); + gv_FlmSysData.pLogger->AddRef(); + gv_FlmSysData.pLogger->unlockLogger(); + } + f_mutexUnlock( gv_FlmSysData.hShareMutex); + break; + + case FLM_USE_ESM: + gv_FlmSysData.bOkToUseESM = (FLMBOOL)((Value1 != 0) + ? (FLMBOOL)TRUE + : (FLMBOOL)FALSE); + break; + + case FLM_ASSIGN_HTTP_SYMS: + //Note: before you attempt this, you had better have loaded the + //necessary shared library... + if ( gv_FlmSysData.HttpConfigParms.fnReg || + gv_FlmSysData.HttpConfigParms.fnDereg || + gv_FlmSysData.HttpConfigParms.fnReqPath || + gv_FlmSysData.HttpConfigParms.fnReqQuery || + gv_FlmSysData.HttpConfigParms.fnReqHdrValue || + gv_FlmSysData.HttpConfigParms.fnSetHdrValue || + gv_FlmSysData.HttpConfigParms.fnPrintf || + gv_FlmSysData.HttpConfigParms.fnEmit || + gv_FlmSysData.HttpConfigParms.fnSetNoCache || + gv_FlmSysData.HttpConfigParms.fnSendHeader || + gv_FlmSysData.HttpConfigParms.fnSetIOMode || + gv_FlmSysData.HttpConfigParms.fnSendBuffer || + gv_FlmSysData.HttpConfigParms.fnAcquireSession || + gv_FlmSysData.HttpConfigParms.fnReleaseSession || + gv_FlmSysData.HttpConfigParms.fnAcquireUser || + gv_FlmSysData.HttpConfigParms.fnReleaseUser || + gv_FlmSysData.HttpConfigParms.fnSetSessionValue || + gv_FlmSysData.HttpConfigParms.fnGetSessionValue || + gv_FlmSysData.HttpConfigParms.fnGetGblValue || + gv_FlmSysData.HttpConfigParms.fnSetGblValue || + gv_FlmSysData.HttpConfigParms.fnRecvBuffer ) + { + rc = RC_SET( FERR_HTTP_SYMS_EXIST); + goto Exit; + } + else + { + gv_FlmSysData.HttpConfigParms.fnReg = ((HTTPCONFIGPARAMS *)Value1)->fnReg; + gv_FlmSysData.HttpConfigParms.fnDereg = ((HTTPCONFIGPARAMS *)Value1)->fnDereg; + gv_FlmSysData.HttpConfigParms.fnReqPath = ((HTTPCONFIGPARAMS *)Value1)->fnReqPath; + gv_FlmSysData.HttpConfigParms.fnReqQuery = ((HTTPCONFIGPARAMS *)Value1)->fnReqQuery; + gv_FlmSysData.HttpConfigParms.fnReqHdrValue = ((HTTPCONFIGPARAMS *)Value1)->fnReqHdrValue; + gv_FlmSysData.HttpConfigParms.fnSetHdrValue = ((HTTPCONFIGPARAMS *)Value1)->fnSetHdrValue; + gv_FlmSysData.HttpConfigParms.fnPrintf = ((HTTPCONFIGPARAMS *)Value1)->fnPrintf; + gv_FlmSysData.HttpConfigParms.fnEmit = ((HTTPCONFIGPARAMS *)Value1)->fnEmit; + gv_FlmSysData.HttpConfigParms.fnSetNoCache = ((HTTPCONFIGPARAMS *)Value1)->fnSetNoCache; + gv_FlmSysData.HttpConfigParms.fnSendHeader = ((HTTPCONFIGPARAMS *)Value1)->fnSendHeader; + gv_FlmSysData.HttpConfigParms.fnSetIOMode = ((HTTPCONFIGPARAMS *)Value1)->fnSetIOMode; + gv_FlmSysData.HttpConfigParms.fnSendBuffer = ((HTTPCONFIGPARAMS *)Value1)->fnSendBuffer; + gv_FlmSysData.HttpConfigParms.fnAcquireSession = ((HTTPCONFIGPARAMS *)Value1)->fnAcquireSession; + gv_FlmSysData.HttpConfigParms.fnReleaseSession = ((HTTPCONFIGPARAMS *)Value1)->fnReleaseSession; + gv_FlmSysData.HttpConfigParms.fnAcquireUser = ((HTTPCONFIGPARAMS *)Value1)->fnAcquireUser; + gv_FlmSysData.HttpConfigParms.fnReleaseUser = ((HTTPCONFIGPARAMS *)Value1)->fnReleaseUser; + gv_FlmSysData.HttpConfigParms.fnSetSessionValue = ((HTTPCONFIGPARAMS *)Value1)->fnSetSessionValue; + gv_FlmSysData.HttpConfigParms.fnGetSessionValue = ((HTTPCONFIGPARAMS *)Value1)->fnGetSessionValue; + gv_FlmSysData.HttpConfigParms.fnGetGblValue = ((HTTPCONFIGPARAMS *)Value1)->fnGetGblValue; + gv_FlmSysData.HttpConfigParms.fnSetGblValue = ((HTTPCONFIGPARAMS *)Value1)->fnSetGblValue; + gv_FlmSysData.HttpConfigParms.fnRecvBuffer = ((HTTPCONFIGPARAMS *)Value1)->fnRecvBuffer; + } + break; + + case FLM_REGISTER_HTTP_URL: + // Value1: FLM_MODULE_HANDLE + // Value2: Url string + if ((Value1 == NULL) || (Value2 == NULL)) + { + rc = RC_SET( FERR_INVALID_PARM); + goto Exit; + } + + rc = flmRegisterHttpCallback((FLM_MODULE_HANDLE)Value1, (char *)Value2); + break; + + case FLM_DEREGISTER_HTTP_URL: + rc = flmDeregisterHttpCallback(); + break; + + case FLM_UNASSIGN_HTTP_SYMS: + gv_FlmSysData.HttpConfigParms.fnReg = NULL; + gv_FlmSysData.HttpConfigParms.fnDereg = NULL; + gv_FlmSysData.HttpConfigParms.fnReqPath = NULL; + gv_FlmSysData.HttpConfigParms.fnReqQuery = NULL; + gv_FlmSysData.HttpConfigParms.fnReqHdrValue = NULL; + gv_FlmSysData.HttpConfigParms.fnSetHdrValue = NULL; + gv_FlmSysData.HttpConfigParms.fnPrintf = NULL; + gv_FlmSysData.HttpConfigParms.fnEmit = NULL; + gv_FlmSysData.HttpConfigParms.fnSetNoCache = NULL; + gv_FlmSysData.HttpConfigParms.fnSendHeader = NULL; + gv_FlmSysData.HttpConfigParms.fnSetIOMode = NULL; + gv_FlmSysData.HttpConfigParms.fnSendBuffer = NULL; + gv_FlmSysData.HttpConfigParms.fnAcquireSession = NULL; + gv_FlmSysData.HttpConfigParms.fnReleaseSession = NULL; + gv_FlmSysData.HttpConfigParms.fnAcquireUser = NULL; + gv_FlmSysData.HttpConfigParms.fnReleaseUser = NULL; + gv_FlmSysData.HttpConfigParms.fnSetSessionValue = NULL; + gv_FlmSysData.HttpConfigParms.fnGetSessionValue = NULL; + gv_FlmSysData.HttpConfigParms.fnGetGblValue = NULL; + gv_FlmSysData.HttpConfigParms.fnSetGblValue = NULL; + gv_FlmSysData.HttpConfigParms.fnRecvBuffer = NULL; + + break; + + case FLM_KILL_DB_HANDLES: + { + FFILE * pTmpFile; + + f_mutexLock( gv_FlmSysData.hShareMutex); + if( Value1) + { + // Look up the file using flmFindFile to see if we have the + // file open. May unlock and re-lock the global mutex. + + if( RC_OK( flmFindFile( (const char *)Value1, + (const char *)Value2, &pTmpFile)) && pTmpFile) + { + flmSetMustCloseFlags( pTmpFile, FERR_OK, TRUE); + } + } + else + { + if( gv_FlmSysData.pFileHashTbl) + { + FLMUINT uiLoop; + + for( uiLoop = 0; uiLoop < FILE_HASH_ENTRIES; uiLoop++) + { + pTmpFile = + (FFILE *)gv_FlmSysData.pFileHashTbl[ uiLoop].pFirstInBucket; + + while( pTmpFile) + { + flmSetMustCloseFlags( pTmpFile, FERR_OK, TRUE); + pTmpFile = pTmpFile->pNext; + } + } + } + } + + f_mutexUnlock( gv_FlmSysData.hShareMutex); + + // Kill all sessions + + if( gv_FlmSysData.pSessionMgr) + { + gv_FlmSysData.pSessionMgr->shutdownSessions(); + } + + break; + } + + case FLM_QUERY_MAX: + f_mutexLock( gv_FlmSysData.hQueryMutex); + gv_FlmSysData.uiMaxQueries = (FLMUINT)Value1; + gv_FlmSysData.bNeedToUnsetMaxQueries = FALSE; + flmFreeSavedQueries( TRUE); + + // flmFreeSavedQueries unlocks the mutex. + + break; + + case FLM_MAX_DIRTY_CACHE: + f_mutexLock( gv_FlmSysData.hShareMutex); + if (!Value1) + { + gv_FlmSysData.SCacheMgr.bAutoCalcMaxDirty = TRUE; + gv_FlmSysData.SCacheMgr.uiMaxDirtyCache = 0; + gv_FlmSysData.SCacheMgr.uiLowDirtyCache = 0; + } + else + { + gv_FlmSysData.SCacheMgr.bAutoCalcMaxDirty = FALSE; + gv_FlmSysData.SCacheMgr.uiMaxDirtyCache = (FLMUINT)Value1; + + // Low threshhold must be no higher than maximum! + + if ((gv_FlmSysData.SCacheMgr.uiLowDirtyCache = + (FLMUINT)Value2) > gv_FlmSysData.SCacheMgr.uiMaxDirtyCache) + { + gv_FlmSysData.SCacheMgr.uiLowDirtyCache = + gv_FlmSysData.SCacheMgr.uiMaxDirtyCache; + } + } + f_mutexUnlock( gv_FlmSysData.hShareMutex); + break; + + case FLM_QUERY_STRATIFY_LIMITS: + f_mutexLock( gv_FlmSysData.hShareMutex); + gv_FlmSysData.uiMaxStratifyIterations = (FLMUINT)Value1; + gv_FlmSysData.uiMaxStratifyTime = (FLMUINT)Value2; + f_mutexUnlock( gv_FlmSysData.hShareMutex); + break; + + default: + rc = RC_SET( FERR_NOT_IMPLEMENTED); + break; + } + +Exit: + return( rc); +} + +/*API~*********************************************************************** +Desc : Gets configured shared attributes. +*END************************************************************************/ +FLMEXP RCODE FLMAPI FlmGetConfig( + eFlmConfigTypes eConfigType, + void * Value1 + ) +{ + RCODE rc = FERR_OK; + FLMUINT uiTmp; + + switch( eConfigType) + { + case FLM_CACHE_LIMIT: + f_mutexLock( gv_FlmSysData.hShareMutex); + f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); + *((FLMUINT *)Value1) = gv_FlmSysData.SCacheMgr.Usage.uiMaxBytes + + gv_FlmSysData.RCacheMgr.Usage.uiMaxBytes; + f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); + f_mutexUnlock( gv_FlmSysData.hShareMutex); + break; + + case FLM_BLOCK_CACHE_PERCENTAGE: + f_mutexLock( gv_FlmSysData.hShareMutex); + *((FLMUINT *)Value1) = gv_FlmSysData.uiBlockCachePercentage; + f_mutexUnlock( gv_FlmSysData.hShareMutex); + break; + + case FLM_SCACHE_DEBUG: +#ifdef FLM_DEBUG + *((FLMBOOL *)Value1) = gv_FlmSysData.SCacheMgr.bDebug; +#else + *((FLMBOOL *)Value1) = FALSE; +#endif + break; + + case FLM_OPEN_FILES: + *((FLMUINT *)Value1) = gv_FlmSysData.pFileHdlMgr->GetOpenedFiles(); + break; + + case FLM_OPEN_THRESHOLD: + *((FLMUINT *)Value1) = gv_FlmSysData.pFileHdlMgr->GetOpenThreshold(); + break; + + case FLM_TMPDIR: + f_mutexLock( gv_FlmSysData.hShareMutex); + + if( !gv_FlmSysData.bTempDirSet ) + { + rc = RC_SET( FERR_IO_PATH_NOT_FOUND ); + + // Set the output to nulls on failure. + + *((char *)Value1) = 0; + } + else + { + f_strcpy( (char *)Value1, gv_FlmSysData.szTempDir); + } + + f_mutexUnlock( gv_FlmSysData.hShareMutex); + break; + + case FLM_MAX_CP_INTERVAL: + FLM_TIMER_UNITS_TO_SECS( gv_FlmSysData.uiMaxCPInterval, uiTmp); + *((FLMUINT *)Value1) = uiTmp; + break; + + case FLM_BLOB_EXT: + f_strcpy( (char *)Value1, (const char *)gv_FlmSysData.ucBlobExt); + break; + + case FLM_MAX_TRANS_SECS: + FLM_TIMER_UNITS_TO_SECS( gv_FlmSysData.uiMaxTransTime, uiTmp); + *((FLMUINT *)Value1) = uiTmp; + break; + + case FLM_MAX_TRANS_INACTIVE_SECS: + FLM_TIMER_UNITS_TO_SECS( gv_FlmSysData.uiMaxTransInactiveTime, uiTmp); + *((FLMUINT *)Value1) = (FLMUINT)uiTmp; + break; + + case FLM_CACHE_ADJUST_INTERVAL: + FLM_TIMER_UNITS_TO_SECS( gv_FlmSysData.uiCacheAdjustInterval, uiTmp); + *((FLMUINT *)Value1) = uiTmp; + break; + + case FLM_CACHE_CLEANUP_INTERVAL: + FLM_TIMER_UNITS_TO_SECS( gv_FlmSysData.uiCacheCleanupInterval, uiTmp); + *((FLMUINT *)Value1) = uiTmp; + break; + + case FLM_UNUSED_CLEANUP_INTERVAL: + FLM_TIMER_UNITS_TO_SECS( gv_FlmSysData.uiUnusedCleanupInterval, uiTmp); + *((FLMUINT *)Value1) = uiTmp; + break; + + case FLM_MAX_UNUSED_TIME: + f_mutexLock( gv_FlmSysData.hShareMutex); + FLM_TIMER_UNITS_TO_SECS( gv_FlmSysData.uiMaxUnusedTime, uiTmp); + f_mutexUnlock( gv_FlmSysData.hShareMutex); + *((FLMUINT *)Value1) = uiTmp; + break; + + case FLM_OUT_OF_MEM_SIMULATION: +#ifdef DEBUG_SIM_OUT_OF_MEM + *((FLMBOOL *)Value1) = + (gv_FlmSysData.uiOutOfMemSimEnabledFlag == + OUT_OF_MEM_SIM_ENABLED_FLAG) + ? TRUE + : FALSE; +#else + *((FLMBOOL *)Value1) = FALSE; +#endif + break; + + case FLM_CACHE_CHECK: + *((FLMBOOL *)Value1) = gv_FlmSysData.bCheckCache; + break; + + case FLM_USE_ESM: + *((FLMBOOL *)Value1) = gv_FlmSysData.bOkToUseESM; + break; + + case FLM_QUERY_MAX: + f_mutexLock( gv_FlmSysData.hQueryMutex); + *((FLMUINT *)Value1) = gv_FlmSysData.uiMaxQueries; + f_mutexUnlock( gv_FlmSysData.hQueryMutex); + break; + + case FLM_MAX_DIRTY_CACHE: + f_mutexLock( gv_FlmSysData.hShareMutex); + *((FLMUINT *)Value1) = gv_FlmSysData.SCacheMgr.uiMaxDirtyCache; + f_mutexUnlock( gv_FlmSysData.hShareMutex); + break; + + case FLM_DYNA_CACHE_SUPPORTED: +#ifdef FLM_CAN_GET_PHYS_MEM + *((FLMBOOL *)Value1) = TRUE; +#else + *((FLMBOOL *)Value1) = FALSE; +#endif + break; + + case FLM_QUERY_STRATIFY_LIMITS: + f_mutexLock( gv_FlmSysData.hShareMutex); + if (Value1) + { + *((FLMUINT *)Value1) = gv_FlmSysData.uiMaxStratifyIterations; + } + f_mutexUnlock( gv_FlmSysData.hShareMutex); + break; + + default: + rc = RC_SET( FERR_NOT_IMPLEMENTED); + break; + } + +//Exit: + return( rc); +} + +/*API~*********************************************************************** +Desc: +*END************************************************************************/ +FLMEXP RCODE FLMAPI FlmGetThreadInfo( + POOL * pPool, + F_THREAD_INFO ** ppThreadInfo, + FLMUINT * puiNumThreads, + const char * pszUrl) +{ + RCODE rc = FERR_OK; + CS_CONTEXT_p pCSContext = NULL; + + if( pszUrl) + { + // flmGetCSConnection may return a NULL CS_CONTEXT if the + // URL references a local resource. + + if( RC_BAD( rc = flmGetCSConnection( pszUrl, &pCSContext))) + { + goto Exit; + } + } + + if( pCSContext) + { + NODE * pTree; + FCL_WIRE Wire; + + Wire.setContext( pCSContext); + + // Send a request get statistics + + if (RC_BAD( Wire.sendOp( FCS_OPCLASS_GLOBAL, + FCS_OP_GLOBAL_GET_THREAD_INFO))) + { + goto Exit; + } + + if (RC_BAD( Wire.sendTerminate())) + { + goto Exit; + } + + // Read the response. + + if (RC_BAD( Wire.read())) + { + goto Exit; + } + + if (RC_BAD( Wire.getRCode())) + { + goto Exit; + } + + if( RC_BAD( Wire.getHTD( pPool, &pTree))) + { + goto Exit; + } + + if( RC_BAD( rc = fcsExtractThreadInfo( pTree, pPool, + ppThreadInfo, puiNumThreads))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = gv_FlmSysData.pThreadMgr->getThreadInfo( pPool, + ppThreadInfo, puiNumThreads))) + { + goto Exit; + } + } + +Exit: + + if( pCSContext) + { + flmCloseCSConnection( &pCSContext); + } + + return( rc); +} + +/**************************************************************************** +Desc: Returns the temporary directory (path) that is part of the share + structure. We have to pass in the FDB structure in order + to know if we should lock a mutex in order to access the + path structure. +****************************************************************************/ +RCODE flmGetTmpDir( + char * pszOutputTmpDir) +{ + RCODE rc = FERR_OK; + + f_mutexLock( gv_FlmSysData.hShareMutex); + + // Return an error if the temp directory has not been set. + + if( !gv_FlmSysData.bTempDirSet) + { + rc = RC_SET( FERR_IO_PATH_NOT_FOUND ); + + // Set the output to nulls on failure. + *pszOutputTmpDir = 0; + } + else + { + f_strcpy( pszOutputTmpDir, gv_FlmSysData.szTempDir); + } + + f_mutexUnlock( gv_FlmSysData.hShareMutex); + return( rc); +} + +/**************************************************************************** +Desc: This shuts down the background threads +Note: This routine assumes that the global mutex is locked. The mutex will + be unlocked internally, but will always be locked on exit. +****************************************************************************/ +FSTATIC void flmShutdownDbThreads( + FFILE * pFile) +{ + RCODE rc = FERR_OK; + F_BKGND_IX * pBackgroundIx; + FDB * pDb; + F_Thread * pThread; + FLMUINT uiThreadId; + FLMUINT uiThreadCount; + FLMBOOL bMutexLocked = TRUE; + + // Shut down the tracker thread + + if( pFile->pMaintThrd) + { + pFile->pMaintThrd->setShutdownFlag(); + f_semSignal( pFile->hMaintSem); + + // Unlock the global mutex + + f_mutexUnlock( gv_FlmSysData.hShareMutex); + bMutexLocked = FALSE; + + pFile->pMaintThrd->stopThread(); + + // Re-lock the mutex + + f_mutexLock( gv_FlmSysData.hShareMutex); + bMutexLocked = TRUE; + + pFile->pMaintThrd->Release(); + pFile->pMaintThrd = NULL; + + f_semDestroy( &pFile->hMaintSem); + } + + // Signal all background threads to shut down that are + // associated with this FFILE + + for( ;;) + { + uiThreadCount = 0; + + // Shut down all background indexing threads. + + uiThreadId = 0; + for( ;;) + { + if( RC_BAD( rc = gv_FlmSysData.pThreadMgr->getNextGroupThread( + &pThread, FLM_BACKGROUND_INDEXING_THREAD_GROUP, &uiThreadId))) + { + if( rc == FERR_NOT_FOUND) + { + rc = FERR_OK; + break; + } + else + { + flmAssert( 0); + } + } + else + { + pBackgroundIx = (F_BKGND_IX *)pThread->getParm1(); + if( pBackgroundIx && pBackgroundIx->pFile == pFile) + { + // Set the thread's terminate flag. + + uiThreadCount++; + pThread->setShutdownFlag(); + } + + pThread->Release(); + pThread = NULL; + } + } + + // Shut down all threads in the FLM_DB_THREAD_GROUP. + + uiThreadId = 0; + for( ;;) + { + if( RC_BAD( rc = gv_FlmSysData.pThreadMgr->getNextGroupThread( + &pThread, FLM_DB_THREAD_GROUP, &uiThreadId))) + { + if( rc == FERR_NOT_FOUND) + { + rc = FERR_OK; + break; + } + else + { + flmAssert( 0); + } + } + else + { + pDb = (FDB *)pThread->getParm2(); + if (pDb && pDb->pFile == pFile) + { + + // Set the thread's terminate flag. + + uiThreadCount++; + pThread->setShutdownFlag(); + } + + pThread->Release(); + pThread = NULL; + } + } + + if( !uiThreadCount) + { + break; + } + + // Unlock the global mutex + + f_mutexUnlock( gv_FlmSysData.hShareMutex); + bMutexLocked = FALSE; + + // Give the threads a chance to terminate + + f_sleep( 50); + + // Re-lock the mutex and see if any threads are still active + + f_mutexLock( gv_FlmSysData.hShareMutex); + bMutexLocked = TRUE; + } + + // Re-lock the mutex + + if( !bMutexLocked) + { + f_mutexLock( gv_FlmSysData.hShareMutex); + } +} + +/**************************************************************************** +Desc: This routine frees all of the structures associated with a file. + Whoever called this routine has already determined that it is safe + to do so. +Notes: The global mutex is assumed to be locked when entering the + routine. It may be unlocked and re-locked before the routine + exits, however. +****************************************************************************/ +void flmFreeFile( + FFILE * pFile) // File to be freed. +{ + FNOTIFY * pCloseNotifies; + + // See if another thread is in the process of closing + // this FFILE. It is possible for this to happen, since + // the monitor thread may have selected this FFILE to be + // closed because it has been unused for a period of time. + // At the same time, a foreground thread could have called + // FlmConfig to close all unused FFILEs. Since flmFreeFile + // may unlock and re-lock the mutex, there is a small window + // of opportunity for both threads to try to free the same + // FFILE. + + if( pFile->uiFlags & DBF_BEING_CLOSED) + { + return; + } + + // Set the DBF_BEING_CLOSED flag + + pFile->uiFlags |= DBF_BEING_CLOSED; + + // Shut down all background threads before shutting down the CP thread. + + flmShutdownDbThreads( pFile); + + // At this point, the use count better be zero + + flmAssert( pFile->uiUseCount == 0); + + // Unlock the mutex + + f_mutexUnlock( gv_FlmSysData.hShareMutex); + + // Shutdown the checkpoint thread + + if( pFile->pCPThrd) + { + pFile->pCPThrd->stopThread(); + pFile->pCPThrd->Release(); + pFile->pCPThrd = NULL; + } + + f_mutexLock( gv_FlmSysData.hShareMutex); + + // Unlink all of the DICTs that are connected to the file. + + while( pFile->pDictList) + { + flmUnlinkDict( pFile->pDictList); + } + + // Take the file out of its name hash bucket, if any. + + if (pFile->uiBucket != 0xFFFF) + { + flmUnlinkFileFromBucket( pFile); + } + + // Unlink the file from the not-used list + + flmUnlinkFileFromNUList( pFile); + + // Free the RFL data, if any. + + if (pFile->pRfl) + { + pFile->pRfl->Release(); + pFile->pRfl = NULL; + } + + // Free any open notify requests associated with the file. + // We shouldn't have any open notifies at this point, but + // we'll be nice and clean up any resources anyway. + + flmAssert( pFile->pOpenNotifies == NULL); + flmFreeNotifyList( &pFile->pOpenNotifies); + + // Save pCloseNotifies -- we will notify any waiters once the + // FFILE has been freed. + + pCloseNotifies = pFile->pCloseNotifies; + + // Free any dictionary usage structures associated with the file. + + flmFreeDictList( &pFile->pDictList); + + // Free any shared cache associated with the file. + + ScaFreeFileCache( pFile); + + // Free any record cache associated with the file. + + flmRcaFreeFileRecs( pFile); + + // Free the file ID list. This will also remove all file handles + // associated with this file. + + if( pFile->pFileIdList) + { + FLMINT iRefCnt; + + iRefCnt = pFile->pFileIdList->Release(); + flmAssert( !iRefCnt); + pFile->pFileIdList = NULL; + } + + // Release the lock objects. + + if( pFile->pWriteLockObj) + { + pFile->pWriteLockObj->Release(); + pFile->pWriteLockObj = NULL; + } + + if( pFile->pFileLockObj) + { + pFile->pFileLockObj->Release(); + pFile->pFileLockObj = NULL; + } + + // Close and delete the lock file. + + if( pFile->pLockFileHdl) + { + (void)pFile->pLockFileHdl->Close(); + pFile->pLockFileHdl->Release(); + pFile->pLockFileHdl = NULL; + } + + // Free the write buffer managers. + + if( pFile->pBufferMgr) + { + pFile->pBufferMgr->Release(); + pFile->pBufferMgr = NULL; + } + + // Free the log header write buffer + + if( pFile->pucLogHdrWriteBuf) + { +#ifdef FLM_WIN + (void)VirtualFree( pFile->pucLogHdrWriteBuf, 0, MEM_RELEASE); + pFile->pucLogHdrWriteBuf = NULL; +#elif defined( FLM_LINUX) || defined( FLM_SOLARIS) + free( pFile->pucLogHdrWriteBuf); + pFile->pucLogHdrWriteBuf = NULL; +#else + f_free( &pFile->pucLogHdrWriteBuf); +#endif + } + + GedPoolFree( &pFile->krefPool); + + if( pFile->ppBlocksDone) + { + f_free( &pFile->ppBlocksDone); + pFile->uiBlocksDoneArraySize = 0; + } + + if( pFile->pECacheMgr) + { + pFile->pECacheMgr->Release(); + pFile->pECacheMgr = NULL; + } + + // Free the maintenance thread's semaphore + + if( pFile->hMaintSem != F_SEM_NULL) + { + f_semDestroy( &pFile->hMaintSem); + } + + // Free the database wrapping key + + if( pFile->pDbWrappingKey) + { + pFile->pDbWrappingKey->Release(); + pFile->pDbWrappingKey = NULL; + } + + // Free the password + + if( pFile->pszDbPassword) + { + f_free( &pFile->pszDbPassword); + } + + // Free the FFILE + + f_free( &pFile); + + // Notify waiters that the FFILE is gone + + while( pCloseNotifies) + { + F_SEM hSem; + + *(pCloseNotifies->pRc) = FERR_OK; + hSem = pCloseNotifies->hSem; + pCloseNotifies = pCloseNotifies->pNext; + f_semSignal( hSem); + } + + // Global mutex is still locked at this point +} + +/**************************************************************************** +Desc: This routine frees a registered event. +****************************************************************************/ +FSTATIC void flmFreeEvent( + FEVENT * pEvent, + F_MUTEX hMutex, + FEVENT ** ppEventListRV + ) +{ + f_mutexLock( hMutex); + if (pEvent->pPrev) + { + pEvent->pPrev->pNext = pEvent->pNext; + } + else + { + *ppEventListRV = pEvent->pNext; + } + if (pEvent->pNext) + { + pEvent->pNext->pPrev = pEvent->pPrev; + } + f_mutexUnlock( hMutex); + f_free( &pEvent); +} + +/************************************************************************ +Desc : Cleans up - assumes that the spin lock has already been + obtained. This allows it to be called directly from + FlmStartup on error conditions. +************************************************************************/ +FSTATIC void flmCleanup( void) +{ + FLMUINT uiCnt; + FLMINT iEventCategory; + + // NOTE: We are checking and decrementing a global variable here. + // However, on platforms that properly support atomic exchange, + // we are OK, because the caller has obtained a spin lock before + // calling this routine, so we are guaranteed to be the only thread + // executing this code at this point. On platforms that don't + // support atomic exchange, our spin lock will be less reliable for + // really tight race conditions. But in reality, nobody should be + // calling FlmStartup and FlmShutdown in race conditions like that + // anyway. We are only doing the spin lock stuff to try and be + // nice about it if they are. + + // This check allows FlmShutdown to be called before calling + // FlmStartup, or even if FlmStartup fails. + + if (!gv_uiFlmSysStartupCount) + { + return; + } + + // If we decrement the count and it doesn't go to zero, we are not + // ready to do cleanup yet. + + if (--gv_uiFlmSysStartupCount > 0) + { + return; + } + + // Deregister the Http Callback function if it has been registered. + // Note: Usually, this is all handled at the SMI level (in + // SMDIBHandle::exit). This code is here for the cases where we're + // part of Flint or some other utility that doesn't necessarily use + // SMI... + if (gv_FlmSysData.HttpConfigParms.fnDereg) + { + FlmConfig( FLM_DEREGISTER_HTTP_URL, NULL, NULL); + } + + // Free any queries that have been saved in the query list. + + if (gv_FlmSysData.hQueryMutex != F_MUTEX_NULL) + { + + // Setting uiMaxQueries to zero will cause flmFreeSavedQueries + // to free the entire list. Also, embedded queries will not be + // added back into the list when uiMaxQueries is zero. + + gv_FlmSysData.uiMaxQueries = 0; + flmFreeSavedQueries( FALSE); + } + + // Shut down the monitor thread, if there is one. + + f_threadDestroy( &gv_FlmSysData.pMonitorThrd); + + // Shut down the session manager + + if( gv_FlmSysData.pSessionMgr) + { + gv_FlmSysData.pSessionMgr->Release(); + gv_FlmSysData.pSessionMgr = NULL; + } + + // Destroy the session mutex + + if( gv_FlmSysData.hHttpSessionMutex != F_MUTEX_NULL) + { + f_mutexDestroy( &gv_FlmSysData.hHttpSessionMutex); + } + + gv_FlmSysData.pMrnuFile = NULL; + gv_FlmSysData.pLrnuFile = NULL; + + /* Free all of the files and associated structures. */ + + if (gv_FlmSysData.pFileHashTbl) + { + FBUCKET * pFileHashTbl; + + // flmFreeFile expects the global mutex to be locked + // IMPORTANT NOTE: pFileHashTbl is ALWAYS allocated + // AFTER the mutex is allocated, so we are guaranteed + // to have a mutex if pFileHashTbl is non-NULL. + + f_mutexLock( gv_FlmSysData.hShareMutex); + for (uiCnt = 0, pFileHashTbl = gv_FlmSysData.pFileHashTbl; + uiCnt < FILE_HASH_ENTRIES; + uiCnt++, pFileHashTbl++) + { + FFILE * pFile = (FFILE *)pFileHashTbl->pFirstInBucket; + FFILE * pTmpFile; + + while( pFile) + { + pTmpFile = pFile; + pFile = pFile->pNext; + flmFreeFile( pTmpFile); + } + pFileHashTbl->pFirstInBucket = NULL; + } + + // Unlock the global mutex + f_mutexUnlock( gv_FlmSysData.hShareMutex); + + // Free the hash table + f_free( &gv_FlmSysData.pFileHashTbl); + } + + // Free the statistics. + + if (gv_FlmSysData.bStatsInitialized) + { + FlmFreeStats( &gv_FlmSysData.Stats); + gv_FlmSysData.bStatsInitialized = FALSE; + } + + // Free (release) FLAIM's File Shared File Handles. + + if (gv_FlmSysData.pFileHdlMgr) + { + FLMINT iRefCnt = gv_FlmSysData.pFileHdlMgr->Release(); + + // No one else should have a reference to the file handle manager + // after this point. + +#ifdef FLM_DEBUG + flmAssert( !iRefCnt); +#else + // Quiet the compiler about the unused variable + (void)iRefCnt; +#endif + gv_FlmSysData.pFileHdlMgr = NULL; + } + + // Free (release) FLAIM's Server Lock Manager. + + if (gv_FlmSysData.pServerLockMgr) + { + FLMINT iRefCnt; + + // Release all locks. + + gv_FlmSysData.pServerLockMgr->CheckLockTimeouts( TRUE); + + // Release the lock manager. + + iRefCnt = gv_FlmSysData.pServerLockMgr->Release(); + + // No one else should have a reference to the server lock manager + // at this point, so lets trip a flmAssert if the object was really + // not deleted. + +#ifdef FLM_DEBUG + flmAssert( !iRefCnt); +#else + // Quiet the compiler about the unused variable + (void)iRefCnt; +#endif + gv_FlmSysData.pServerLockMgr = NULL; + } + + // Free the resources of the shared cache manager. + + ScaExit(); + + // Free the resources of the record cache manager. + + flmRcaExit(); + + // Free the mutexes last of all. + + if (gv_FlmSysData.hQueryMutex != F_MUTEX_NULL) + { + f_mutexDestroy( &gv_FlmSysData.hQueryMutex); + } + + if (gv_FlmSysData.hShareMutex != F_MUTEX_NULL) + { + f_mutexDestroy( &gv_FlmSysData.hShareMutex); + } + + if (gv_FlmSysData.hFileHdlMutex != F_MUTEX_NULL) + { + f_mutexDestroy( &gv_FlmSysData.hFileHdlMutex); + } + + if (gv_FlmSysData.hServerLockMgrMutex != F_MUTEX_NULL) + { + f_mutexDestroy( &gv_FlmSysData.hServerLockMgrMutex); + } + + if (gv_FlmSysData.HttpConfigParms.hMutex != F_MUTEX_NULL) + { + f_mutexDestroy( &gv_FlmSysData.HttpConfigParms.hMutex); + } + + + // Free up callbacks that have been registered for events. + + for (iEventCategory = 0; + iEventCategory < F_MAX_EVENT_CATEGORIES; + iEventCategory++) + { + if (gv_FlmSysData.EventHdrs [iEventCategory].hMutex != F_MUTEX_NULL) + { + while (gv_FlmSysData.EventHdrs [iEventCategory].pEventCBList) + { + flmFreeEvent( + gv_FlmSysData.EventHdrs [iEventCategory].pEventCBList, + gv_FlmSysData.EventHdrs [iEventCategory].hMutex, + &gv_FlmSysData.EventHdrs [iEventCategory].pEventCBList); + } + f_mutexDestroy( &gv_FlmSysData.EventHdrs [iEventCategory].hMutex); + } + } + + // Free (release) FLAIM's File Shared File System Object. + + if (gv_FlmSysData.pFileSystem) + { + FLMINT iRefCnt = gv_FlmSysData.pFileSystem->Release(); + + // No one else should have a reference to the file system + // after this point. + +#ifdef FLM_DEBUG + flmAssert( !iRefCnt); +#else + // Quiet the compiler about the unused variable + (void)iRefCnt; +#endif + gv_FlmSysData.pFileSystem = NULL; + } +#ifdef FLM_DBG_LOG + flmDbgLogExit(); +#endif + + // Free the serial number generator + + f_freeSerialNumberGenerator(); + +#if defined( FLM_NLM) + nssUninitialize(); +#endif + + // Release the logger (if any) + + if( gv_FlmSysData.pLogger) + { + gv_FlmSysData.pLogger->lockLogger(); + if( gv_FlmSysData.pLogger->Release() >= 1) + { + gv_FlmSysData.pLogger->unlockLogger(); + } + gv_FlmSysData.pLogger = NULL; + } + + // Release the thread manager + + if( gv_FlmSysData.pThreadMgr) + { + gv_FlmSysData.pThreadMgr->Release(); + gv_FlmSysData.pThreadMgr = NULL; + } + + // Release the slab manager + + if( gv_FlmSysData.pSlabManager) + { + gv_FlmSysData.pSlabManager->Release(); + gv_FlmSysData.pSlabManager = NULL; + } + + // Shutdown NICI + +#ifdef FLM_USE_NICI + CCS_Shutdown(); +#endif + + // Memory cleanup needs to be last. + + f_memoryCleanup(); + +#ifdef FLM_NLM + if( gv_bNetWareStartupCalled) + { + f_netwareShutdown(); + gv_bNetWareStartupCalled = FALSE; + } +#endif +} + +/*API~*********************************************************************** +Desc : Shuts down FLAIM. +Notes: Allows itself to be called multiple times and even before FlmStartup + is called, or even if FlmStartup fails. Warning: May not handle + race conditions very well on platforms that do not support atomic + exchange. +*END************************************************************************/ +FLMEXP void FLMAPI FlmShutdown( void) +{ + flmLockSysData(); + flmCleanup(); + flmUnlockSysData(); +} + +/**************************************************************************** +Desc: This routine determines the hash bucket for a string. +****************************************************************************/ +FLMUINT flmStrHashBucket( + const char * pszStr, + FBUCKET * pHashTbl, + FLMUINT uiNumBuckets) +{ + FLMUINT uiHashIndex; + + if ((uiHashIndex = (FLMUINT)*pszStr) >= uiNumBuckets) + { + uiHashIndex -= uiNumBuckets; + } + + while( *pszStr) + { + if ((uiHashIndex = + (FLMUINT)((pHashTbl [uiHashIndex].uiHashValue) ^ (FLMUINT)(*pszStr))) >= + uiNumBuckets) + uiHashIndex -= uiNumBuckets; + pszStr++; + } + return( uiHashIndex); +} + +/**************************************************************************** +Desc: This routine determines the hash bucket for a binary array of + characters. +****************************************************************************/ +FLMUINT flmBinHashBucket( + void * pBuf, + FLMUINT uiBufLen, + FBUCKET * pHashTbl, + FLMUINT uiNumBuckets) +{ + FLMUINT uiHashIndex; + FLMBYTE * ptr = (FLMBYTE *)pBuf; + + if ((uiHashIndex = (FLMUINT)*ptr) >= uiNumBuckets) + uiHashIndex -= uiNumBuckets; + while (uiBufLen) + { + if ((uiHashIndex = + (FLMUINT)((pHashTbl [uiHashIndex].uiHashValue) ^ (FLMUINT)(*ptr))) >= + uiNumBuckets) + uiHashIndex -= uiNumBuckets; + ptr++; + uiBufLen--; + } + return( uiHashIndex); +} + +/**************************************************************************** +Desc: This routine links a notify request into a notification list and + then waits to be notified that the event has occurred. +Notes: + This routine assumes that the shared mutex is locked and that + it is supposed to unlock it. +****************************************************************************/ +RCODE flmWaitNotifyReq( + F_MUTEX hMutex, + FNOTIFY ** ppNotifyListRV, /* Pointer to the head of a notify + list where the new notify + request should be linked into. */ + void * UserData /* Other user data that the notifier + can use to pass other information + to the waiter. */ + ) +{ + FNOTIFY * pNotify = NULL; + RCODE TempRc; + RCODE rc = FERR_OK; + F_SEM hSem; + + /* First create a notify request and link it into the list. */ + + if (RC_OK( rc = f_calloc( (FLMUINT)(sizeof( FNOTIFY)), &pNotify))) + { + + /* Allocate a semaphore for the notify request. */ + + pNotify->uiThreadId = f_threadId(); + pNotify->hSem = F_SEM_NULL; + rc = f_semCreate( &pNotify->hSem); + } + if (RC_BAD( rc)) + { + if (pNotify) + { + if (pNotify->hSem != F_SEM_NULL) + { + f_semDestroy( &pNotify->hSem); + } + f_free( &pNotify); + } + goto Exit; + } + pNotify->pRc = &rc; + pNotify->UserData = UserData; + pNotify->pNext = *ppNotifyListRV; + *ppNotifyListRV = pNotify; + hSem = pNotify->hSem; + + /* Unlock the mutex and wait on the semaphore. */ + + f_mutexUnlock( hMutex); + if (RC_BAD( TempRc = f_semWait( hSem, F_SEM_WAITFOREVER))) + { + rc = TempRc; + } + + /* Free the semaphore and the notify structure. */ + + f_semDestroy( &hSem); + f_free( &pNotify); + + // Relock the mutex + + f_mutexLock( hMutex); + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: This routine links an FFILE structure to its name hash bucket. + NOTE: This function assumes that the global mutex has been + locked. +****************************************************************************/ +RCODE flmLinkFileToBucket( + FFILE * pFile /* File to be linked to its name hash bucket. */ + ) +{ + RCODE rc = FERR_OK; + FFILE * pTmpFile; + FBUCKET * pBucket; + FLMUINT uiBucket; + char szDbPathStr [F_PATH_MAX_SIZE]; + + pBucket = gv_FlmSysData.pFileHashTbl; + + // Normalize the path to a string before hashing on it. + + if (RC_BAD( rc = f_pathToStorageString( pFile->pszDbPath, szDbPathStr))) + { + goto Exit; + } + + uiBucket = flmStrHashBucket( szDbPathStr, pBucket, FILE_HASH_ENTRIES); + pBucket = &pBucket [uiBucket]; + if (pBucket->pFirstInBucket) + { + pTmpFile = (FFILE *)pBucket->pFirstInBucket; + pTmpFile->pPrev = pFile; + } + pFile->uiBucket = uiBucket; + pFile->pPrev = (FFILE *)NULL; + pFile->pNext = (FFILE *)pBucket->pFirstInBucket; + pBucket->pFirstInBucket = pFile; + +Exit: + + return( rc); +} + + +/**************************************************************************** +Desc: This routine unlinks an FFILE structure from its name hash bucket. + NOTE: This function assumes that the global mutex has been + locked. +****************************************************************************/ +FSTATIC void flmUnlinkFileFromBucket( + FFILE * pFile /* Pointer to file that is to be unlinked from + its name hash bucket. */ + ) +{ + if (pFile->uiBucket != 0xFFFF) + { + if (pFile->pPrev) + { + pFile->pPrev->pNext = pFile->pNext; + } + else + { + gv_FlmSysData.pFileHashTbl [pFile->uiBucket].pFirstInBucket = pFile->pNext; + } + if (pFile->pNext) + { + pFile->pNext->pPrev = pFile->pPrev; + } + pFile->uiBucket = 0xFFFF; + } +} + +/**************************************************************************** +Desc: This routine links an FFILE structure to the unused list. + NOTE: This function assumes that the global mutex has been + locked. +****************************************************************************/ +void flmLinkFileToNUList( + FFILE * pFile, + FLMBOOL bQuickTimeout) +{ + + if( !bQuickTimeout) + { + pFile->pPrevNUFile = NULL; + if ((pFile->pNextNUFile = gv_FlmSysData.pMrnuFile) == NULL) + { + gv_FlmSysData.pLrnuFile = pFile; + } + else + { + pFile->pNextNUFile->pPrevNUFile = pFile; + } + + gv_FlmSysData.pMrnuFile = pFile; + pFile->uiZeroUseCountTime = (FLMUINT)FLM_GET_TIMER(); + } + else + { + pFile->pNextNUFile = NULL; + if ((pFile->pPrevNUFile = gv_FlmSysData.pLrnuFile) == NULL) + { + gv_FlmSysData.pMrnuFile = pFile; + } + else + { + pFile->pPrevNUFile->pNextNUFile = pFile; + } + + gv_FlmSysData.pLrnuFile = pFile; + pFile->uiZeroUseCountTime = 0; + } + + pFile->uiFlags |= DBF_IN_NU_LIST; + + if (pFile->pRfl) + { + pFile->pRfl->closeFile(); + } +} + +/**************************************************************************** +Desc: This routine unlinks an FFILE structure from the unused list. + NOTE: This function assumes that the global mutex has been + locked. +****************************************************************************/ +void flmUnlinkFileFromNUList( + FFILE * pFile /* File to be unlinked from unused list. */ + ) +{ + if (pFile->uiFlags & DBF_IN_NU_LIST) + { + if (!pFile->pPrevNUFile) + { + gv_FlmSysData.pMrnuFile = pFile->pNextNUFile; + } + else + { + pFile->pPrevNUFile->pNextNUFile = pFile->pNextNUFile; + } + if (!pFile->pNextNUFile) + { + gv_FlmSysData.pLrnuFile = pFile->pPrevNUFile; + } + else + { + pFile->pNextNUFile->pPrevNUFile = pFile->pPrevNUFile; + } + pFile->pPrevNUFile = pFile->pNextNUFile = (FFILE *)NULL; + pFile->uiFlags &= ~(DBF_IN_NU_LIST); + } +} + +/**************************************************************************** +Desc: This routine checks unused structures to see if any have been unused + longer than the maximum unused time. If so, it frees them up. +Note: This routine assumes that the calling routine has locked the global + mutex prior to calling this routine. The mutex may be unlocked and + re-locked by one of the called routines. +****************************************************************************/ +void flmCheckNUStructs( + FLMUINT uiCurrTime) +{ + FFILE * pFile; + + if (!uiCurrTime) + { + uiCurrTime = FLM_GET_TIMER(); + } + + // Look for unused FFILEs + + pFile = gv_FlmSysData.pLrnuFile; + for (;;) + { + // Break out of the loop as soon as we discover an unused FFILE + // structure which has not been unused the maximum number of seconds. + + if (pFile && + (FLM_ELAPSED_TIME( uiCurrTime, pFile->uiZeroUseCountTime) >= + gv_FlmSysData.uiMaxUnusedTime || !pFile->uiZeroUseCountTime)) + { + + // Remove the FFILE from memory. + + flmFreeFile( pFile); + + // flmFreeFile may have unlocked (and re-locked the global mutex, + // so we need to start at the beginning of the list again. + + // Need to unlock the mutex here in case another thread is in + // the process of closing the last FFILE. If we hang on to + // the mutex, it will never be able to get back in and finish + // the job. + + f_mutexUnlock( gv_FlmSysData.hShareMutex); + f_yieldCPU(); + f_mutexLock( gv_FlmSysData.hShareMutex); + pFile = gv_FlmSysData.pLrnuFile; + } + else + { + break; + } + } + + // Look for unused file handles + + if( gv_FlmSysData.pFileHdlMgr) + { + gv_FlmSysData.pFileHdlMgr->CheckAgedItems( + gv_FlmSysData.pFileHdlMgr->GetMaxAvailTime()); + } +} + +/**************************************************************************** +Desc: This routine unlinks an FDICT structure from its FFILE structure and + then frees the FDICT structure. + NOTE: This routine assumes that the global mutex is locked. +****************************************************************************/ +void flmUnlinkDict( + FDICT * pDict /* FDICT that is to be unlinked from its FFFILE + structure. */ + ) +{ + /* + Now unlink the local dictionary from its file - if it is connected + to one. + */ + + if (pDict->pFile) + { + if (pDict->pPrev) + { + pDict->pPrev->pNext = pDict->pNext; + } + else + { + pDict->pFile->pDictList = pDict->pNext; + } + if (pDict->pNext) + { + pDict->pNext->pPrev = pDict->pPrev; + } + } + + /* Finally, free the local dictionary and its associated tables. */ + + flmFreeDict( pDict); +} + +/**************************************************************************** +Desc: This routine links an FDB structure to an FFILE structure. + NOTE: This routine assumes that the global mutex has been + locked. +****************************************************************************/ +RCODE flmLinkFdbToFile( + FDB * pDb, /* FDB that is to be linked to an FFILE + structure. */ + FFILE * pFile /* Pointer to FFILE structure the FDB is to be + linked to. */ + ) +{ + RCODE rc = FERR_OK; + + /* + If the use count on the file used to be zero, unlink it from the + unused list. + */ + + flmAssert( !pDb->pFile); + pDb->pPrevForFile = NULL; + if ((pDb->pNextForFile = pFile->pFirstDb) != NULL) + { + pFile->pFirstDb->pPrevForFile = pDb; + } + pFile->pFirstDb = pDb; + pDb->pFile = pFile; + if (++pFile->uiUseCount == 1) + { + flmUnlinkFileFromNUList( pFile); + } + if (pDb->uiFlags & FDB_INTERNAL_OPEN) + { + pFile->uiInternalUseCount++; + } + + /* + Allocate the super file object + */ + + if (!pDb->pSFileHdl) + { + if( (pDb->pSFileHdl = f_new F_SuperFileHdl) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + /* + Set up the super file + */ + + if( RC_BAD( rc = pDb->pSFileHdl->Setup( pFile->pFileIdList, + pFile->pszDbPath, + pFile->pszDataDir))) + { + goto Exit; + } + + if( pFile->pECacheMgr) + { + pDb->pSFileHdl->setECacheMgr( pFile->pECacheMgr); + } + + if( pFile->FileHdr.uiVersionNum) + { + pDb->pSFileHdl->SetBlockSize( pFile->FileHdr.uiBlockSize); + pDb->pSFileHdl->SetDbVersion( pFile->FileHdr.uiVersionNum); + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: This routine unlinks an FDB structure from its FFILE structure. + NOTE: This routine assumes that the global mutex has been + locked. +****************************************************************************/ +void flmUnlinkFdbFromFile( + FDB * pDb) +{ + FFILE * pFile; + + if ((pFile = pDb->pFile) != NULL) + { + + // Unlink the FDB from the FFILE. + + if (pDb->pNextForFile) + { + pDb->pNextForFile->pPrevForFile = pDb->pPrevForFile; + } + if (pDb->pPrevForFile) + { + pDb->pPrevForFile->pNextForFile = pDb->pNextForFile; + } + else + { + pFile->pFirstDb = pDb->pNextForFile; + } + pDb->pNextForFile = pDb->pPrevForFile = NULL; + pDb->pFile = NULL; + + // Decrement use counts in the FFILE. + + if (pDb->uiFlags & FDB_INTERNAL_OPEN) + { + flmAssert( pFile->uiInternalUseCount); + pFile->uiInternalUseCount--; + } + + // If the use count goes to zero on the file, put the file + // into the unused list. + + flmAssert( pFile->uiUseCount); + if (!(--pFile->uiUseCount)) + { + // If the "must close" flag is set, it indicates that + // the FFILE is being forced to close. Put the FFILE in + // the NU list, but specify that it should be quickly + // timed-out. + + flmLinkFileToNUList( pFile, pFile->bMustClose); + } + } +} + +/**************************************************************************** +Desc: This routine functions as a thread. It monitors open files and + frees up files which have been closed longer than the maximum + close time. +****************************************************************************/ +RCODE flmMonitor( + F_Thread * pThread) +{ + FLMUINT uiLastUnusedCleanupTime = 0; + FLMUINT uiLastRCacheCleanupTime = 0; + FLMUINT uiLastSCacheCleanupTime = 0; + FLMUINT uiCurrTime; + FLMUINT uiMaxLockTime; +#ifdef FLM_CAN_GET_PHYS_MEM + FLMUINT uiLastCacheAdjustTime = 0; +#endif + + FLM_MILLI_TO_TIMER_UNITS( 100, uiMaxLockTime); + + for (;;) + { + + /* See if we should shut down. */ + + if( pThread->getShutdownFlag()) + { + break; + } + + uiCurrTime = FLM_GET_TIMER(); + + // Check the not used stuff and lock timeouts. + + if ( FLM_ELAPSED_TIME( uiCurrTime, uiLastUnusedCleanupTime) >= + gv_FlmSysData.uiUnusedCleanupInterval || + (gv_FlmSysData.pLrnuFile && + !gv_FlmSysData.pLrnuFile->uiZeroUseCountTime)) + { + // See if any unused structures have bee unused longer than the + // maximum unused time. Free them if they have. + // May unlock and re-lock the global mutex. + + f_mutexLock( gv_FlmSysData.hShareMutex); + flmCheckNUStructs( 0); + f_mutexUnlock( gv_FlmSysData.hShareMutex); + + // Reset the timer + + uiCurrTime = uiLastUnusedCleanupTime = FLM_GET_TIMER(); + } + + // Call the lock manager to check timeouts. It is critial + // that this routine be called on a regular interval to + // timeout lock waiters that have expired. + + gv_FlmSysData.pServerLockMgr->CheckLockTimeouts( FALSE); + + // Check the adjusting cache limit + +#ifdef FLM_CAN_GET_PHYS_MEM + if ((gv_FlmSysData.bDynamicCacheAdjust) && + (FLM_ELAPSED_TIME( uiCurrTime, uiLastCacheAdjustTime) >= + gv_FlmSysData.uiCacheAdjustInterval)) + { + FLMUINT uiCacheBytes; + + f_mutexLock( gv_FlmSysData.hShareMutex); + f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); + + // Make sure the dynamic adjust flag is still set. + + if ((gv_FlmSysData.bDynamicCacheAdjust) && + (FLM_ELAPSED_TIME( uiCurrTime, uiLastCacheAdjustTime) >= + gv_FlmSysData.uiCacheAdjustInterval)) + { + uiCacheBytes = flmGetCacheBytes( gv_FlmSysData.uiCacheAdjustPercent, + gv_FlmSysData.uiCacheAdjustMin, + gv_FlmSysData.uiCacheAdjustMax, + gv_FlmSysData.uiCacheAdjustMinToLeave, TRUE, + gv_FlmSysData.SCacheMgr.Usage.uiTotalBytesAllocated + + gv_FlmSysData.RCacheMgr.pRCacheAlloc->getTotalBytesAllocated()); + (void)flmSetCacheLimits( uiCacheBytes, FALSE); + } + f_mutexUnlock( gv_FlmSysData.hShareMutex); + f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); + uiCurrTime = uiLastCacheAdjustTime = FLM_GET_TIMER(); + } +#endif + + // See if block cache should be cleaned up + + if ((gv_FlmSysData.uiCacheCleanupInterval) && + (FLM_ELAPSED_TIME( uiCurrTime, uiLastSCacheCleanupTime) >= + gv_FlmSysData.uiCacheCleanupInterval)) + { + ScaCleanupCache( uiMaxLockTime); + uiCurrTime = uiLastSCacheCleanupTime = FLM_GET_TIMER(); + } + + // See if record cache should be cleaned up + + if( (gv_FlmSysData.uiCacheCleanupInterval) && + (FLM_ELAPSED_TIME( uiCurrTime, uiLastRCacheCleanupTime) >= + gv_FlmSysData.uiCacheCleanupInterval)) + { + flmRcaCleanupCache( uiMaxLockTime, FALSE); + uiCurrTime = uiLastRCacheCleanupTime = FLM_GET_TIMER(); + } + + // Cleanup old sessions + + if( gv_FlmSysData.pSessionMgr) + { + gv_FlmSysData.pSessionMgr->timeoutInactiveSessions( + MAX_SESSION_INACTIVE_SECS, FALSE); + } + + f_sleep( 1000); + } + + return( FERR_OK); +} + +/*API~*********************************************************************** +Desc : Registers a callback function to receive events. +*END************************************************************************/ +FLMEXP RCODE FLMAPI FlmRegisterForEvent( + FEventCategory eCategory, + FEVENT_CB fnEventCB, + void * pvAppData, + HFEVENT * phEventRV) +{ + RCODE rc = FERR_OK; + FEVENT * pEvent; + + *phEventRV = HFEVENT_NULL; + + // Make sure it is a legal event category to register for. + + if (eCategory >= F_MAX_EVENT_CATEGORIES) + { + rc = RC_SET( FERR_NOT_IMPLEMENTED); + goto Exit; + } + + // Allocate an event structure + + if (RC_BAD( rc = f_calloc( (FLMUINT)(sizeof( FEVENT)), &pEvent))) + { + goto Exit; + } + *phEventRV = (HFEVENT)pEvent; + + // Initialize the structure members and linkt to the + // list of events off of the event category. + + pEvent->eCategory = eCategory; + pEvent->fnEventCB = fnEventCB; + pEvent->pvAppData = pvAppData; + // pEvent->pPrev = NULL; // done by flmAlloc above. + + // Mutex should be locked to link into list. + + f_mutexLock( gv_FlmSysData.EventHdrs [eCategory].hMutex); + if ((pEvent->pNext = + gv_FlmSysData.EventHdrs [eCategory].pEventCBList) != NULL) + { + pEvent->pNext->pPrev = pEvent; + } + gv_FlmSysData.EventHdrs [eCategory].pEventCBList = pEvent; + f_mutexUnlock( gv_FlmSysData.EventHdrs [eCategory].hMutex); + +Exit: + return( rc); +} + +/*API~*********************************************************************** +Desc : Deregisters a callback function that was registered to receive events. +*END************************************************************************/ +FLMEXP void FLMAPI FlmDeregisterForEvent( + HFEVENT * phEventRV) +{ + if (phEventRV && *phEventRV != HFEVENT_NULL) + { + FEVENT * pEvent = (FEVENT *)(*phEventRV); + + if (pEvent->eCategory < F_MAX_EVENT_CATEGORIES) + { + flmFreeEvent( pEvent, + gv_FlmSysData.EventHdrs [pEvent->eCategory].hMutex, + &gv_FlmSysData.EventHdrs [pEvent->eCategory].pEventCBList); + } + *phEventRV = HFEVENT_NULL; + } +} + +/**************************************************************************** +Desc: This routine does an event callback. Note that the mutex is + locked during the callback. +****************************************************************************/ +void flmDoEventCallback( + FEventCategory eCategory, + FEventType eEventType, + void * pvEventData1, + void * pvEventData2) +{ + FEVENT * pEvent; + + f_mutexLock( gv_FlmSysData.EventHdrs [eCategory].hMutex); + pEvent = gv_FlmSysData.EventHdrs [eCategory].pEventCBList; + while (pEvent) + { + (*pEvent->fnEventCB)( eEventType, pEvent->pvAppData, + pvEventData1, + pvEventData2); + pEvent = pEvent->pNext; + } + f_mutexUnlock( gv_FlmSysData.EventHdrs [eCategory].hMutex); +} + +/**************************************************************************** +Desc: This routine sets the "must close" flags on the FFILE and its FDBs +****************************************************************************/ +void flmSetMustCloseFlags( + FFILE * pFile, + RCODE rcMustClose, + FLMBOOL bMutexLocked) +{ + FDB * pTmpDb; + + if( !bMutexLocked) + { + f_mutexLock( gv_FlmSysData.hShareMutex); + } + + if( !pFile->bMustClose) + { + pFile->bMustClose = TRUE; + pFile->rcMustClose = rcMustClose; + pTmpDb = pFile->pFirstDb; + while( pTmpDb) + { + pTmpDb->bMustClose = TRUE; + pTmpDb = pTmpDb->pNextForFile; + } + + // Log a message indicating why the "must close" flag has been + // set. Calling flmCheckFFileState with the bMustClose flag + // already set to TRUE will cause a message to be logged. + + (void)flmCheckFFileState( pFile); + } + + if( !bMutexLocked) + { + f_mutexUnlock( gv_FlmSysData.hShareMutex); + } +} + +/**************************************************************************** +Desc: Constructor +****************************************************************************/ +F_Session::F_Session() +{ + m_pSessionMgr = NULL; + m_uiThreadId = 0; + m_uiThreadLockCount = 0; + m_hMutex = F_MUTEX_NULL; + m_pNotifyList = NULL; + m_pPrev = NULL; + m_pNext = NULL; + m_pNameTable = NULL; + m_uiNameTableFFileId = 0; + m_uiDictSeqNum = 0; + m_uiLastUsed = FLM_GET_TIMER(); + m_uiNextToken = FLM_GET_TIMER(); + m_pXmlImport = NULL; + m_pXmlExport = NULL; + m_pDbTable = NULL; + f_memset( m_ucKey, 0, sizeof( m_ucKey)); +} + +/**************************************************************************** +Desc: Destructor +****************************************************************************/ +F_Session::~F_Session() +{ + flmAssert( !m_pPrev); + flmAssert( !m_pNext); + flmAssert( !m_i32RefCnt); + flmAssert( !m_uiThreadLockCount); + + // Wake up any waiters + + signalLockWaiters( FERR_FAILURE, FALSE); + + // Free the session mutex + + if( m_hMutex != F_MUTEX_NULL) + { + f_mutexDestroy( &m_hMutex); + } + + // Clean up any database objects + + if( m_pDbTable) + { + m_pDbTable->Release(); + } + + // Free the name table + + if( m_pNameTable) + { + m_pNameTable->Release(); + } + + // Free XML import / export + + if( m_pXmlImport) + { + m_pXmlImport->Release(); + } + + if( m_pXmlExport) + { + m_pXmlExport->Release(); + } +} + +/**************************************************************************** +Desc: Signals the next thread waiting to acquire the lock on the session + if rc == FERR_OK. Otherwise, signals all waiting threads. +****************************************************************************/ +void F_Session::signalLockWaiters( + RCODE rc, + FLMBOOL bMutexLocked) +{ + F_SEM hSem; + + if( m_pNotifyList) + { + if( !bMutexLocked) + { + f_mutexLock( m_hMutex); + } + + while( m_pNotifyList) + { + *(m_pNotifyList->pRc) = rc; + hSem = m_pNotifyList->hSem; + m_pNotifyList = m_pNotifyList->pNext; + f_semSignal( hSem); + if( RC_OK( rc)) + { + break; + } + } + + if( !bMutexLocked) + { + f_mutexUnlock( m_hMutex); + } + } +} + +/**************************************************************************** +Desc: Configures a session for use. +****************************************************************************/ +RCODE F_Session::setupSession( + F_SessionMgr * pSessionMgr) +{ + RCODE rc = FERR_OK; + + flmAssert( m_hMutex == F_MUTEX_NULL); + flmAssert( pSessionMgr); + + if( RC_BAD( rc = f_mutexCreate( &m_hMutex))) + { + goto Exit; + } + + if( (m_pXmlImport = f_new F_XMLImport) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = m_pXmlImport->setup())) + { + goto Exit; + } + + if( (m_pXmlExport = f_new F_XMLExport) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = m_pXmlExport->setup())) + { + goto Exit; + } + + // Allocate the object table + + if( (m_pDbTable = f_new F_HashTable) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = m_pDbTable->setupHashTable( FALSE, 16, + pSessionMgr->getCRCTable()))) + { + goto Exit; + } + + m_pSessionMgr = pSessionMgr; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Adds a database handle to the session's list of handles. Once added + to the session, the calling code should not close the handle directly. +****************************************************************************/ +RCODE F_Session::addDbHandle( + HFDB hDb, + char * pucKey) +{ + RCODE rc = FERR_OK; + F_SessionDb * pSessionDb = NULL; + void * pvKey; + FLMUINT uiKeyLen; + + if( (pSessionDb = f_new F_SessionDb) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = pSessionDb->setupSessionDb( this, hDb))) + { + goto Exit; + } + + if( RC_BAD( rc = m_pDbTable->addObject( pSessionDb))) + { + goto Exit; + } + + if( pucKey) + { + pvKey = pSessionDb->getKey( &uiKeyLen); + flmAssert( uiKeyLen == F_SESSION_DB_KEY_LEN); + f_memcpy( pucKey, (FLMBYTE *)pvKey, uiKeyLen); + } + + pSessionDb->Release(); + pSessionDb = NULL; + +Exit: + + if( pSessionDb) + { + pSessionDb->m_hDb = HFDB_NULL; + pSessionDb->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: Closes the specific database identified by the passed-in key +****************************************************************************/ +void F_Session::closeDb( + const char * pucKey) +{ + (void)m_pDbTable->getObject( (void *)pucKey, F_SESSION_DB_KEY_LEN, + NULL, TRUE); +} + +/**************************************************************************** +Desc: Gets a specific database handle from the session given the passed-in + key +****************************************************************************/ +RCODE F_Session::getDbHandle( + const char * pucKey, + HFDB * phDb) +{ + RCODE rc = FERR_OK; + F_SessionDb * pSessionDb = NULL; + F_HashObject * pObject; + + *phDb = HFDB_NULL; + + if( RC_BAD( rc = m_pDbTable->getObject( (void *)pucKey, F_SESSION_DB_KEY_LEN, + &pObject, FALSE))) + { + if( rc == FERR_NOT_FOUND) + { + rc = RC_SET( FERR_BAD_HDL); + } + + goto Exit; + } + + pSessionDb = (F_SessionDb *)pObject; + *phDb = pSessionDb->m_hDb; + +Exit: + + if( pSessionDb) + { + pSessionDb->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: Returns the next database handle in the global list of handles managed + by the session. +****************************************************************************/ +RCODE F_Session::getNextDb( + F_SessionDb ** ppSessionDb) +{ + F_HashObject * pObject = *ppSessionDb; + RCODE rc = FERR_OK; + + if( RC_BAD( rc = m_pDbTable->getNextObjectInGlobal( &pObject))) + { + goto Exit; + } + + *ppSessionDb = (F_SessionDb *)pObject; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Releases any session resources associated with the specified FFILE +****************************************************************************/ +void F_Session::releaseFileResources( + FFILE * pFile) +{ + F_HashObject * pObject; + F_HashObject * pNextObject; + F_SessionDb * pSessionDb; + + // Close all database handles with the specified FFILE + + pObject = NULL; + if( RC_BAD( m_pDbTable->getNextObjectInGlobal( &pObject))) + { + goto Exit; + } + + while( pObject) + { + if( (pNextObject = pObject->getNextInGlobal()) != NULL) + { + pNextObject->AddRef(); + } + + if( pObject->objectType() == HASH_DB_OBJ) + { + pSessionDb = (F_SessionDb *)pObject; + + if( ((FDB *)pSessionDb->getDbHandle())->pFile == pFile) + { + closeDb( (const char *)pSessionDb->getKey()); + } + } + pObject->Release(); + pObject = pNextObject; + } + +Exit: + + return; +} + +/**************************************************************************** +Desc: Returns a pointer to a name table generated based on the supplied + database handle +****************************************************************************/ +RCODE F_Session::getNameTable( + HFDB hDb, + F_NameTable ** ppNameTable) +{ + FLMUINT uiSeq; + FLMUINT uiFFileId; + RCODE rc = FERR_OK; + + if( !m_pNameTable) + { + if( (m_pNameTable = f_new F_NameTable) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + } + + if( RC_BAD( rc = FlmDbGetConfig( hDb, + FDB_GET_DICT_SEQ_NUM, (void *)&uiSeq))) + { + goto Exit; + } + + if( RC_BAD( rc = FlmDbGetConfig( hDb, + FDB_GET_FFILE_ID, (void *)&uiFFileId))) + { + goto Exit; + } + + // If the database handle does not reference the same + // database or dictionary as the last time the name table + // was refreshed, we need to re-populate the table. + + if( uiSeq != m_uiDictSeqNum || + m_uiNameTableFFileId != uiFFileId) + { + if( RC_BAD( rc = m_pNameTable->setupFromDb( hDb))) + { + goto Exit; + } + + m_uiDictSeqNum = uiSeq; + m_uiNameTableFFileId = uiFFileId; + } + + *ppNameTable = m_pNameTable; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Returns a pointer to a name table generated based on the supplied + FFILE. +****************************************************************************/ +RCODE F_Session::getNameTable( + FFILE * pFile, + F_NameTable ** ppNameTable) +{ + FDB * pDb = NULL; + RCODE rc = FERR_OK; + + // Temporarily open the database + + if( RC_BAD( rc = flmOpenFile( pFile, NULL, NULL, NULL, 0, + TRUE, NULL, NULL, + pFile->pszDbPassword, &pDb))) + { + goto Exit; + } + + // Get the name table + + if( RC_BAD( rc = getNameTable( (HFDB)pDb, ppNameTable))) + { + goto Exit; + } + +Exit: + + if( pDb) + { + (void) FlmDbClose( (HFDB *)&pDb); + } + + return( rc); +} + +/**************************************************************************** +Desc: Returns a unique token generated by the session manager. Tokens are + used to help make handles passed to clients unique across server + executions +****************************************************************************/ +FLMUINT F_Session::getNextToken( void) +{ + return( m_pSessionMgr->getNextToken()); +} + +/**************************************************************************** +Desc: Locks a session for use by a thread. If bWait is TRUE, the thread + will be put into a queue until the lock can be granted. This routine + can be called multiple times by the same thread, as long as + unlockSession is called a corresponding number of times. +****************************************************************************/ +RCODE F_Session::lockSession( + FLMBOOL bWait) +{ + RCODE rc = FERR_OK; + + flmAssert( m_i32RefCnt); + f_mutexLock( m_hMutex); + + if( m_uiThreadId && m_uiThreadId != f_threadId()) + { + if( !bWait) + { + rc = RC_SET( FERR_TIMEOUT); + goto Exit; + } + + if( RC_BAD( rc = flmWaitNotifyReq( m_hMutex, &m_pNotifyList, NULL))) + { + goto Exit; + } + } + + m_uiThreadId = f_threadId(); + m_uiThreadLockCount++; + +Exit: + + f_mutexUnlock( m_hMutex); + return( rc); +} + +/**************************************************************************** +Desc: Releases a thread's lock on a session. +****************************************************************************/ +void F_Session::unlockSession() +{ + F_SEM hSem; + + flmAssert( m_i32RefCnt); + + f_mutexLock( m_hMutex); + if( m_uiThreadId != f_threadId()) + { + flmAssert( 0); + } + else + { + if( --m_uiThreadLockCount == 0) + { + m_uiThreadId = 0; + + if( m_pNotifyList) + { + *(m_pNotifyList->pRc) = FERR_OK; + hSem = m_pNotifyList->hSem; + m_pNotifyList = m_pNotifyList->pNext; + f_semSignal( hSem); + } + } + + m_uiLastUsed = FLM_GET_TIMER(); + } + f_mutexUnlock( m_hMutex); +} + +/**************************************************************************** +Desc: Gets the session object's hash key +****************************************************************************/ +void * F_Session::getKey( + FLMUINT * puiKeyLen) +{ + if( puiKeyLen) + { + *puiKeyLen = (FLMUINT)sizeof( m_ucKey); + } + return( (void *)(&m_ucKey[ 0])); +} + +/**************************************************************************** +Desc: Adds a reference to the session object. The mutex is locked prior + to incrementing the count since multiple threads are allowed to + acquire a pointer to the object. However, they shouldn't use the + object w/o first locking it via a call to lockSession. +****************************************************************************/ +FLMINT F_Session::AddRef( void) +{ + FLMINT iRefCnt; + + f_mutexLock( m_hMutex); + flmAssert( m_i32RefCnt); + iRefCnt = F_Base::AddRef(); + f_mutexUnlock( m_hMutex); + + return( iRefCnt); +} + +/**************************************************************************** +Desc: Decrements the objects use count +****************************************************************************/ +FLMINT F_Session::Release( void) +{ + FLMINT iRefCnt; + + flmAssert( m_i32RefCnt); + + f_mutexLock( m_hMutex); + if( (iRefCnt = --m_i32RefCnt) == 0) + { + f_mutexUnlock( m_hMutex); + delete this; + return( iRefCnt); + } + f_mutexUnlock( m_hMutex); + + return( iRefCnt); +} + +/**************************************************************************** +Desc: Destructor +****************************************************************************/ +F_SessionMgr::~F_SessionMgr() +{ + if( m_pSessionTable) + { + shutdownSessions(); + m_pSessionTable->Release(); + } + + if( m_hMutex != F_MUTEX_NULL) + { + f_mutexDestroy( &m_hMutex); + } + + if( m_pCRCTable) + { + f_freeCRCTable( &m_pCRCTable); + } +} + +/**************************************************************************** +Desc: Releases all resources (database handles, etc.) in all sessions + if they are tied to the specified FFILE +****************************************************************************/ +void F_SessionMgr::releaseFileResources( + FFILE * pFile) +{ + F_Session * pSession; + F_HashObject * pObject; + FLMBOOL bMutexLocked = FALSE; + + if( m_hMutex == F_MUTEX_NULL) + { + goto Exit; + } + + f_mutexLock( m_hMutex); + bMutexLocked = TRUE; + + pObject = NULL; + if( RC_BAD( m_pSessionTable->getNextObjectInGlobal( &pObject))) + { + goto Exit; + } + + while( pObject) + { + flmAssert( pObject->objectType() == HASH_SESSION_OBJ); + + pSession = (F_Session *)pObject; + if( (pObject = pObject->getNextInGlobal()) != NULL) + { + pObject->AddRef(); + } + + if( RC_OK( pSession->lockSession())) + { + pSession->releaseFileResources( pFile); + pSession->unlockSession(); + } + pSession->Release(); + } + +Exit: + if (bMutexLocked) + { + f_mutexUnlock( m_hMutex); + } + + return; +} + +/**************************************************************************** +Desc: Shuts down all sessions being managed +****************************************************************************/ +void F_SessionMgr::shutdownSessions() +{ + F_Session * pSession; + F_HashObject * pObject; + FLMBOOL bMutexLocked = FALSE; + + if( m_hMutex == F_MUTEX_NULL) + { + goto Exit; + } + + f_mutexLock( m_hMutex); + bMutexLocked = TRUE; + + pObject = NULL; + if( RC_BAD( m_pSessionTable->getNextObjectInGlobal( &pObject))) + { + goto Exit; + } + + while( pObject) + { + flmAssert( pObject->objectType() == HASH_SESSION_OBJ); + + pSession = (F_Session *)pObject; + if( (pObject = pObject->getNextInGlobal()) != NULL) + { + pObject->AddRef(); + } + + if( RC_OK( pSession->lockSession())) + { + m_pSessionTable->removeObject( pSession); + pSession->signalLockWaiters( FERR_FAILURE, FALSE); + pSession->unlockSession(); + } + pSession->Release(); + } + +Exit: + if (bMutexLocked) + { + f_mutexUnlock( m_hMutex); + } + + return; +} + +/**************************************************************************** +Desc: Configures the session manager prior to use +****************************************************************************/ +RCODE F_SessionMgr::setupSessionMgr( void) +{ + RCODE rc = FERR_OK; + + flmAssert( m_hMutex == F_MUTEX_NULL); + + // Create the mutex + + if( RC_BAD( rc = f_mutexCreate( &m_hMutex))) + { + goto Exit; + } + + // Initialize the CRC table + + if( RC_BAD( rc = f_initCRCTable( &m_pCRCTable))) + { + goto Exit; + } + + // Create the session object table + + if( (m_pSessionTable = f_new F_HashTable) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = m_pSessionTable->setupHashTable( FALSE, + 128, m_pCRCTable))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Gets a session given a session key +****************************************************************************/ +RCODE F_SessionMgr::getSession( + const char * pszKey, + F_Session ** ppSession) +{ + RCODE rc = FERR_OK; + F_Session * pSession = NULL; + F_HashObject * pObject; + FLMBOOL bMutexLocked = FALSE; + + *ppSession = NULL; + + f_mutexLock( m_hMutex); + bMutexLocked = TRUE; + + if( RC_BAD( rc = m_pSessionTable->getObject( (void *)pszKey, + F_SESSION_KEY_LEN, &pObject, FALSE))) + { + goto Exit; + } + + // NOTE: getObject() does an addRef for the caller. + + pSession = (F_Session *)pObject; + + f_mutexUnlock( m_hMutex); + bMutexLocked = FALSE; + + if( RC_BAD( rc = pSession->lockSession())) + { + pSession->Release(); + goto Exit; + } + + *ppSession = pSession; + +Exit: + + if( bMutexLocked) + { + f_mutexUnlock( m_hMutex); + } + + return( rc); +} + +/**************************************************************************** +Desc: Unlocks and releases a session being used by a thread +****************************************************************************/ +void F_SessionMgr::releaseSession( + F_Session ** ppSession) +{ + (*ppSession)->unlockSession(); + (*ppSession)->Release(); + *ppSession = NULL; +} + +/**************************************************************************** +Desc: Creates a new session +****************************************************************************/ +RCODE F_SessionMgr::createSession( + F_Session ** ppSession) +{ + F_Session * pNewSession = NULL; + FLMBOOL bMutexLocked = FALSE; + RCODE rc = FERR_OK; + + if( (pNewSession = f_new F_Session) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = pNewSession->setupSession( this))) + { + goto Exit; + } + + f_mutexLock( m_hMutex); + bMutexLocked = TRUE; + + // Session ID + + f_sprintf( (char *)&pNewSession->m_ucKey[ 0], "%0*X", + (int)(sizeof( FLMUINT) * 2), + (unsigned)m_uiNextId++); + + // Token + + f_sprintf( (char *)&pNewSession->m_ucKey[ sizeof( FLMUINT) * 2], "%0*X", + (int)(sizeof( FLMUINT) * 2), + (unsigned)m_uiNextToken++); + + pNewSession->m_ucKey[ sizeof( pNewSession->m_ucKey) - 1] = 0; + + // Add the session to the table + + if( RC_BAD( rc = m_pSessionTable->addObject( + pNewSession))) + { + goto Exit; + } + + f_mutexUnlock( m_hMutex); + bMutexLocked = FALSE; + + if( RC_BAD( rc = pNewSession->lockSession())) + { + pNewSession->Release(); + goto Exit; + } + + *ppSession = pNewSession; + pNewSession = NULL; + +Exit: + + if( pNewSession) + { + pNewSession->Release(); + } + + if( bMutexLocked) + { + f_mutexUnlock( m_hMutex); + } + + return( rc); +} + +/**************************************************************************** +Desc: Kills any unused sessions that have been inactive for the specified + number of seconds +****************************************************************************/ +void F_SessionMgr::timeoutInactiveSessions( + FLMUINT uiInactiveSecs, + FLMBOOL bWaitForLocks) +{ + F_Session * pSession; + F_HashObject * pObject; + FLMUINT uiCurrTime; + FLMUINT uiElapTime; + FLMUINT uiElapSecs; + FLMBOOL bMutexLocked = FALSE; + + f_mutexLock( m_hMutex); + bMutexLocked = TRUE; + + pObject = NULL; + if( RC_BAD( m_pSessionTable->getNextObjectInGlobal( &pObject))) + { + goto Exit; + } + + while( pObject) + { + flmAssert( pObject->objectType() == HASH_SESSION_OBJ); + + pSession = (F_Session *)pObject; + if( (pObject = pObject->getNextInGlobal()) != NULL) + { + pObject->AddRef(); + } + + if( RC_OK( pSession->lockSession( bWaitForLocks))) + { + uiCurrTime = FLM_GET_TIMER(); + uiElapTime = FLM_ELAPSED_TIME( uiCurrTime, pSession->m_uiLastUsed); + FLM_TIMER_UNITS_TO_SECS( uiElapTime, uiElapSecs); + + if( !uiInactiveSecs || uiElapSecs >= uiInactiveSecs) + { + m_pSessionTable->removeObject( pSession); + pSession->signalLockWaiters( FERR_FAILURE, FALSE); + } + pSession->unlockSession(); + } + pSession->Release(); + } + +Exit: + + if( bMutexLocked) + { + f_mutexUnlock( m_hMutex); + } +} + +/**************************************************************************** +Desc: Constructor +****************************************************************************/ +F_HashTable::F_HashTable() +{ + m_hMutex = F_MUTEX_NULL; + m_pGlobalList = NULL; + m_ppHashTable = NULL; + m_uiBuckets = 0; + m_pCRCTable = NULL; + m_bOwnCRCTable = FALSE; +} + +/**************************************************************************** +Desc: Destructor +****************************************************************************/ +F_HashTable::~F_HashTable() +{ + F_HashObject * pCur; + F_HashObject * pNext; + + pCur = m_pGlobalList; + while( pCur) + { + pNext = pCur->m_pNextInGlobal; + unlinkObject( pCur); + pCur->Release(); + pCur = pNext; + } + + if( m_ppHashTable) + { + f_free( &m_ppHashTable); + } + + if( m_hMutex != F_MUTEX_NULL) + { + f_mutexDestroy( &m_hMutex); + } + + if( m_pCRCTable && m_bOwnCRCTable) + { + f_freeCRCTable( &m_pCRCTable); + } +} + +/**************************************************************************** +Desc: Configures the hash table prior to first use +****************************************************************************/ +RCODE F_HashTable::setupHashTable( + FLMBOOL bMultithreaded, + FLMUINT uiNumBuckets, + FLMUINT32 * pCRCTable) +{ + RCODE rc = FERR_OK; + + flmAssert( uiNumBuckets); + + // Create the hash table + + if( RC_BAD( rc = f_alloc( + sizeof( F_HashObject *) * uiNumBuckets, &m_ppHashTable))) + { + goto Exit; + } + + m_uiBuckets = uiNumBuckets; + f_memset( m_ppHashTable, 0, sizeof( F_HashObject *) * uiNumBuckets); + + if( bMultithreaded) + { + // Initialize the mutex + + if( RC_BAD( rc = f_mutexCreate( &m_hMutex))) + { + goto Exit; + } + } + + if( !pCRCTable) + { + // Initialize the CRC table + + if( RC_BAD( rc = f_initCRCTable( &m_pCRCTable))) + { + goto Exit; + } + m_bOwnCRCTable = TRUE; + } + else + { + m_pCRCTable = pCRCTable; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Retrieves an object from the hash table with the specified key. + This routine assumes the table's mutex has already been locked. + A reference IS NOT added to the object for the caller. +****************************************************************************/ +RCODE F_HashTable::findObject( + void * pvKey, + FLMUINT uiKeyLen, + F_HashObject ** ppObject) +{ + F_HashObject * pObject = NULL; + FLMUINT uiBucket; + FLMUINT32 ui32CRC = 0; + RCODE rc = FERR_OK; + + *ppObject = NULL; + + // Calculate the hash bucket and mutex offset + + uiBucket = getHashBucket( pvKey, uiKeyLen, &ui32CRC); + + // Search the bucket for an object with a matching + // key. + + pObject = m_ppHashTable[ uiBucket]; + while( pObject) + { + if( pObject->getKeyCRC() == ui32CRC) + { + void * pvTmpKey; + FLMUINT uiTmpKeyLen; + + pvTmpKey = pObject->getKey( &uiTmpKeyLen); + if( uiTmpKeyLen == uiKeyLen && + f_memcmp( pvTmpKey, pvKey, uiKeyLen) == 0) + { + break; + } + } + pObject = pObject->m_pNextInBucket; + } + + if( !pObject) + { + rc = RC_SET( FERR_NOT_FOUND); + goto Exit; + } + + *ppObject = pObject; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Adds an object to the hash table +****************************************************************************/ +RCODE F_HashTable::addObject( + F_HashObject * pObject) +{ + FLMUINT uiBucket; + F_HashObject * pTmp; + void * pvKey; + FLMUINT uiKeyLen; + FLMUINT32 ui32CRC; + FLMBOOL bMutexLocked = FALSE; + RCODE rc = FERR_OK; + + // Calculate and set the objects hash bucket + + flmAssert( pObject->getHashBucket() == F_INVALID_HASH_BUCKET); + + pvKey = pObject->getKey( &uiKeyLen); + flmAssert( uiKeyLen); + + uiBucket = getHashBucket( pvKey, uiKeyLen, &ui32CRC); + pObject->setKeyCRC( ui32CRC); + + // Lock the mutex + + if( m_hMutex != F_MUTEX_NULL) + { + f_mutexLock( m_hMutex); + bMutexLocked = TRUE; + } + + // Make sure the object doesn't already exist + + if( RC_BAD( rc = findObject( pvKey, uiKeyLen, &pTmp))) + { + if( rc != FERR_NOT_FOUND) + { + goto Exit; + } + rc = FERR_OK; + } + else + { + flmAssert( 0); + rc = RC_SET( FERR_EXISTS); + goto Exit; + } + + // Add a reference to the object + + pObject->AddRef(); + + // Link the object into the appropriate lists + + linkObject( pObject, uiBucket); + +Exit: + + // Unlock the mutex + + if( bMutexLocked) + { + f_mutexUnlock( m_hMutex); + } + + return( rc); +} + +/**************************************************************************** +Desc: Returns the next object in the linked list of objects in the hash + table. If *ppObject == NULL, the first object will be returned. +****************************************************************************/ +RCODE F_HashTable::getNextObjectInGlobal( + F_HashObject ** ppObject) +{ + FLMBOOL bMutexLocked = FALSE; + F_HashObject * pOldObj; + RCODE rc = FERR_OK; + + // Lock the mutex + + if( m_hMutex != F_MUTEX_NULL) + { + f_mutexLock( m_hMutex); + bMutexLocked = TRUE; + } + + if( !(*ppObject)) + { + *ppObject = m_pGlobalList; + } + else + { + pOldObj = *ppObject; + *ppObject = (*ppObject)->m_pNextInGlobal; + pOldObj->Release(); + } + + if( *ppObject == NULL) + { + rc = RC_SET( FERR_EOF_HIT); + goto Exit; + } + + (*ppObject)->AddRef(); + +Exit: + + if( bMutexLocked) + { + f_mutexUnlock( m_hMutex); + } + + return( rc); +} + +/**************************************************************************** +Desc: Retrieves an object from the hash table with the specified key +****************************************************************************/ +RCODE F_HashTable::getObject( + void * pvKey, + FLMUINT uiKeyLen, + F_HashObject ** ppObject, + FLMBOOL bRemove) +{ + F_HashObject * pObject; + FLMBOOL bMutexLocked = FALSE; + RCODE rc = FERR_OK; + + // Lock the mutex + + if( m_hMutex != F_MUTEX_NULL) + { + f_mutexLock( m_hMutex); + bMutexLocked = TRUE; + } + + // Search for an object with a matching key. + + if( RC_BAD( rc = findObject( pvKey, uiKeyLen, &pObject))) + { + goto Exit; + } + + if( pObject && bRemove) + { + unlinkObject( pObject); + if( !ppObject) + { + pObject->Release(); + pObject = NULL; + } + } + + if( ppObject) + { + if( !bRemove) + { + pObject->AddRef(); + } + *ppObject = pObject; + pObject = NULL; + } + +Exit: + + if( bMutexLocked) + { + f_mutexUnlock( m_hMutex); + } + + return( rc); +} + +/**************************************************************************** +Desc: Removes an object from the hash table by key +****************************************************************************/ +RCODE F_HashTable::removeObject( + void * pvKey, + FLMUINT uiKeyLen) +{ + return( getObject( pvKey, uiKeyLen, NULL, TRUE)); +} + +/**************************************************************************** +Desc: Removes an object from the hash table by object pointer +****************************************************************************/ +RCODE F_HashTable::removeObject( + F_HashObject * pObject) +{ + FLMUINT uiKeyLen; + void * pvKey; + + pvKey = pObject->getKey( &uiKeyLen); + return( getObject( pvKey, uiKeyLen, NULL, TRUE)); +} + +/**************************************************************************** +Desc: Calculates the hash bucket of a key and optionally returns the key's + CRC. +****************************************************************************/ +FLMUINT F_HashTable::getHashBucket( + void * pvKey, + FLMUINT uiLen, + FLMUINT32 * pui32KeyCRC) +{ + FLMUINT32 ui32CRC = 0; + + f_updateCRC( m_pCRCTable, (FLMBYTE *)pvKey, uiLen, &ui32CRC); + if( pui32KeyCRC) + { + *pui32KeyCRC = ui32CRC; + } + return( ui32CRC % m_uiBuckets); +} + +/**************************************************************************** +Desc: Links an object to the global list and also to its bucket +Notes: This routine assumes that the bucket's mutex is already locked + if the hash table is multi-threaded. +****************************************************************************/ +void F_HashTable::linkObject( + F_HashObject * pObject, + FLMUINT uiBucket) +{ + flmAssert( uiBucket < m_uiBuckets); + flmAssert( pObject->getHashBucket() == F_INVALID_HASH_BUCKET); + + // Set the object's bucket + + pObject->setHashBucket( uiBucket); + + // Link the object to its hash bucket + + pObject->m_pNextInBucket = m_ppHashTable[ uiBucket]; + if( m_ppHashTable[ uiBucket]) + { + m_ppHashTable[ uiBucket]->m_pPrevInBucket = pObject; + } + m_ppHashTable[ uiBucket] = pObject; + + // Link to the global list + + pObject->m_pNextInGlobal = m_pGlobalList; + if( m_pGlobalList) + { + m_pGlobalList->m_pPrevInGlobal = pObject; + } + m_pGlobalList = pObject; +} + +/**************************************************************************** +Desc: Unlinks an object from its bucket and the global list. +Notes: This routine assumes that the bucket's mutex is already locked + if the hash table is multi-threaded. +****************************************************************************/ +void F_HashTable::unlinkObject( + F_HashObject * pObject) +{ + FLMUINT uiBucket = pObject->getHashBucket(); + + // Is the bucket valid? + + flmAssert( uiBucket < m_uiBuckets); + + // Unlink from the hash bucket + + if( pObject->m_pNextInBucket) + { + pObject->m_pNextInBucket->m_pPrevInBucket = pObject->m_pPrevInBucket; + } + + if( pObject->m_pPrevInBucket) + { + pObject->m_pPrevInBucket->m_pNextInBucket = pObject->m_pNextInBucket; + } + else + { + m_ppHashTable[ uiBucket] = pObject->m_pNextInBucket; + } + + pObject->m_pPrevInBucket = NULL; + pObject->m_pNextInBucket = NULL; + pObject->setHashBucket( F_INVALID_HASH_BUCKET); + + // Unlink from the global list + + if( pObject->m_pNextInGlobal) + { + pObject->m_pNextInGlobal->m_pPrevInGlobal = pObject->m_pPrevInGlobal; + } + + if( pObject->m_pPrevInGlobal) + { + pObject->m_pPrevInGlobal->m_pNextInGlobal = pObject->m_pNextInGlobal; + } + else + { + m_pGlobalList = pObject->m_pNextInGlobal; + } + + pObject->m_pPrevInGlobal = NULL; + pObject->m_pNextInGlobal = NULL; +} + +/**************************************************************************** +Desc: Constructor +****************************************************************************/ +F_SessionDb::F_SessionDb() +{ + m_hDb = HFDB_NULL; + f_memset( m_ucKey, 0, sizeof( m_ucKey)); +} + +/**************************************************************************** +Desc: Destructor +****************************************************************************/ +F_SessionDb::~F_SessionDb() +{ + if( m_hDb != HFDB_NULL) + { + FlmDbClose( &m_hDb); + } +} + +/**************************************************************************** +Desc: Configures a database object prior to being used +****************************************************************************/ +RCODE F_SessionDb::setupSessionDb( + F_Session * pSession, + HFDB hDb) +{ + flmAssert( hDb != HFDB_NULL); + flmAssert( m_hDb == HFDB_NULL); + + m_pSession = pSession; + m_hDb = hDb; + + // Handle + + f_sprintf( (char *)&m_ucKey[ 0], "%0*X", + (int)(sizeof( FLMUINT) * 2), + (unsigned)((FLMUINT)hDb)); + + // Token + + f_sprintf( (char *)&m_ucKey[ sizeof( FLMUINT) * 2], "%0*X", + (int)(sizeof( FLMUINT) * 2), + (unsigned)m_pSession->getNextToken()); + + m_ucKey[ sizeof( m_ucKey) - 1] = 0; + return( FERR_OK); +} + +/**************************************************************************** +Desc: Returns the key and key length of a database object +****************************************************************************/ +void * F_SessionDb::getKey( + FLMUINT * puiKeyLen) +{ + if( puiKeyLen) + { + *puiKeyLen = (FLMUINT)sizeof( m_ucKey); + } + return( (void *)(&m_ucKey[ 0])); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMEXP RCODE FLMAPI FlmAllocFileSystem( + F_FileSystem ** ppFileSystem) +{ + RCODE rc = FERR_OK; + + flmAssert( ppFileSystem && *ppFileSystem == NULL); + + if( (*ppFileSystem = f_new F_FileSystemImp) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMEXP RCODE FLMAPI FlmAllocDirHdl( + F_DirHdl ** ppDirHdl) +{ + RCODE rc = FERR_OK; + + flmAssert( ppDirHdl && *ppDirHdl == NULL); + + if( (*ppDirHdl = f_new F_DirHdlImp) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMEXP RCODE FLMAPI FlmAllocFileHandle( + F_FileHdl ** ppFileHandle) +{ + RCODE rc = FERR_OK; + + flmAssert( ppFileHandle && *ppFileHandle == NULL); + + if( (*ppFileHandle = f_new F_FileHdlImp) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Deletes (releases) and F_CCS objected referenced in the ITT table. +*****************************************************************************/ +void flmDeleteCCSRefs( + FDICT * pDict + ) +{ + FLMUINT uiLoop; + F_CCS * pCcs = NULL; + ITT * pItt; + + if (pDict && pDict->pIttTbl) + { + for ( pItt = pDict->pIttTbl, uiLoop = 0; + uiLoop < pDict->uiIttCnt; pItt++, uiLoop++) + { + if (ITT_IS_ENCDEF(pItt)) + { + pCcs = (F_CCS *)pItt->pvItem; + pItt->pvItem = NULL; + if (pCcs) + { + pCcs->Release(); + pCcs = NULL; + } + } + } + } +} diff --git a/flaim/src/ftk.h b/flaim/src/ftk.h index 6ad171c..d0ece84 100644 --- a/flaim/src/ftk.h +++ b/flaim/src/ftk.h @@ -1055,15 +1055,20 @@ #include #include #include - #ifndef FLM_OSX - #include - #endif #include #include #include #include #include - + + #ifndef FLM_OSX + #include + #endif + + #ifdef FLM_AIX + #include + #endif + #define FSTATIC static #define f_stricmp(str1,str2) \ @@ -1777,53 +1782,534 @@ FLMINT f_timeCompareTimeStamps( #endif /**************************************************************************** - Atomic Increment, Decrement, Exchange Functions +Desc: Atomic Increment, Decrement, Exchange Functions +Note: Some of this code is derived from the Ximian source code contained + in that Mono project's atomic.h file. ****************************************************************************/ -#ifdef ATOMIC_INCDEC_SUPPORT - #undef ATOMIC_INCDEC_SUPPORT +#ifdef FLM_USE_SPIN_LOCK_ATOMICS + #undef FLM_USE_SPIN_LOCK_ATOMICS #endif -FLMUINT32 ftkAtomicIncrement( - FLMUINT32 * puiTarget); +#ifdef FLM_USE_MUTEX_ATOMICS + #undef FLM_USE_MUTEX_ATOMICS +#endif -FLMUINT32 ftkAtomicDecrement( - FLMUINT32 * puiTarget); +#if defined( FLM_NLM) -FLMUINT32 ftkAtomicExchange( - FLMUINT32 * puiTarget, - FLMUINT32 uiValue); + /********************************************************************** + Desc: + **********************************************************************/ + FINLINE FLMINT32 ftkAtomicIncrement( + volatile FLMINT32 * pi32Target) + { + return( nlm_atomic_inc( pi32Target)); + } + + /********************************************************************** + Desc: + **********************************************************************/ + FINLINE FLMINT32 ftkAtomicDecrement( + volatile FLMINT32 * pi32Target) + { + return( nlm_atomic_dec( pi32Target)); + } + + /********************************************************************** + Desc: + **********************************************************************/ + FINLINE FLMINT32 ftkAtomicExchange( + volatile FLMINT32 * pi32Target, + FLMINT32 i32NewVal) + { + return( atomic_xchg( pi32Target, i32NewVal)); + } -#if defined( FLM_WIN) +#elif defined( FLM_WIN) - #define ftkAtomicIncrement( puiTarget) \ - ((FLMUINT32)InterlockedIncrement( (LPLONG)(puiTarget))) + /********************************************************************** + Desc: + **********************************************************************/ + FINLINE FLMINT32 ftkAtomicIncrement( + volatile FLMINT32 * pi32Target) + { + return( InterlockedIncrement( (volatile LONG *)pi32Target)); + } - #define ftkAtomicDecrement( puiTarget) \ - ((FLMUINT32)InterlockedDecrement( (LPLONG)(puiTarget))) + /********************************************************************** + Desc: + **********************************************************************/ + FINLINE FLMINT32 ftkAtomicDecrement( + volatile FLMINT32 * pi32Target) + { + return( InterlockedDecrement( (volatile LONG *)pi32Target)); + } + + /********************************************************************** + Desc: + **********************************************************************/ + FINLINE FLMINT32 ftkAtomicExchange( + volatile FLMINT32 * pi32Target, + FLMINT32 i32NewVal) + { + return( InterlockedExchange( (volatile LONG *)pi32Target, i32NewVal)); + } + +#elif defined( FLM_AIX) - #define ftkAtomicExchange( puiTarget, uiValue) \ - ((FLMUINT32)InterlockedExchange( (LPLONG)(puiTarget), (LONG)(uiValue))) + /********************************************************************** + Desc: + **********************************************************************/ + FINLINE FLMINT32 ftkAtomicIncrement( + volatile FLMINT32 * pi32Target) + { + return( fetch_and_add( pi32Target, 1)); + } + + /********************************************************************** + Desc: + **********************************************************************/ + FINLINE FLMINT32 ftkAtomicDecrement( + volatile FLMINT32 * pi32Target) + { + return( fetch_and_add( pi32Target, -1)); + } + + /********************************************************************** + Desc: + **********************************************************************/ + FINLINE FLMINT32 ftkAtomicExchange( + volatile FLMINT32 * pi32Target, + FLMINT32 i32NewVal) + { + FLMINT32 i32OldVal; + + for( ;;) + { + i32OldVal = *pi32Target; + + if( compare_and_swap( pi32Target, &i32OldVal, i32NewVal)) + { + break; + } + } + + return( i32OldVal); + } + +#elif defined( FLM_HPUX) - #define ATOMIC_INCDEC_SUPPORT 1 + #define FLM_USE_MUTEX_ATOMICS + +#elif defined( __GNUC__) -#elif defined( FLM_NLM) + #if defined( __i386__) || defined( __x86_64__) - #define ftkAtomicExchange( puiTarget, uiValue) \ - ((FLMUINT32)atomic_xchg( (LONG *)(puiTarget), (LONG)(uiValue))) + /********************************************************************** + Desc: + **********************************************************************/ + FINLINE FLMINT32 ftkAtomicIncrement( + volatile FLMINT32 * pi32Target) + { + FLMINT32 i32Tmp; + + __asm__ __volatile__ ("lock; xaddl %0, %1" + : "=r" (i32Tmp), "=m" (*pi32Target) + : "0" (1), "m" (*pi32Target)); + + return( i32Tmp + 1); + } + + /********************************************************************** + Desc: + **********************************************************************/ + FINLINE FLMINT32 ftkAtomicDecrement( + volatile FLMINT32 * pi32Target) + { + FLMINT32 i32Tmp; + + __asm__ __volatile__ ("lock; xaddl %0, %1" + : "=r" (i32Tmp), "=m" (*pi32Target) + : "0" (-1), "m" (*pi32Target)); + + return( i32Tmp - 1); + } + + /********************************************************************** + Desc: + **********************************************************************/ + FINLINE FLMINT32 ftkAtomicExchange( + volatile FLMINT32 * pi32Target, + FLMINT32 i32NewVal) + { + FLMINT32 i32Ret; + + __asm__ __volatile__ ("1:; lock; cmpxchgl %2, %0; jne 1b" + : "=m" (*pi32Target), "=a" (i32Ret) + : "r" (i32NewVal), "m" (*pi32Target), "a" (*pi32Target)); + + return( i32Ret); + } + + #elif defined( sparc) || defined ( __sparc__) - #define ftkAtomicIncrement( puiTarget) \ - ((FLMUINT32)nlm_AtomicIncrement( puiTarget)) + /********************************************************************** + Desc: + **********************************************************************/ + #define BEGIN_SPIN(tmp,lock) \ + __asm__ __volatile__("1: ldstub [%1],%0\n\t" \ + " cmp %0, 0\n\t" \ + " bne 1b\n\t" \ + " nop" \ + : "=&r" (tmp) \ + : "r" (&lock) \ + : "memory"); + + /********************************************************************** + Desc: + **********************************************************************/ + #define END_SPIN(lock) \ + __asm__ __volatile__("stb %%g0, [%0]" \ + : /* no outputs */ \ + : "r" (&lock)\ + : "memory"); + + #define FLM_USE_SPIN_LOCK_ATOMICS + + #elif defined( __ppc__) || defined ( __powerpc__) + + /********************************************************************** + Desc: + **********************************************************************/ + FINLINE FLMINT32 ftkAtomicIncrement( + volatile FLMINT32 * pi32Target) + { + FLMINT32 i32Result = 0; + FLMINT32 i32Tmp; + + __asm__ __volatile__ ("\n1:\n\t" + "lwarx %0, 0, %2\n\t" + "addi %1, %0, 1\n\t" + "stwcx. %1, 0, %2\n\t" + "bne- 1b" + : "=&b" (i32Result), "=&b" (i32Tmp) + : "r" (i32Target) : "cc", "memory"); + + return( i32Result + 1); + } + + /********************************************************************** + Desc: + **********************************************************************/ + FINLINE FLMINT32 ftkAtomicDecrement( + volatile FLMINT32 * pi32Target) + { + FLMINT32 i32Result = 0; + FLMINT32 i32Tmp; + + __asm__ __volatile__ ("\n1:\n\t" + "lwarx %0, 0, %2\n\t" + "addi %1, %0, -1\n\t" + "stwcx. %1, 0, %2\n\t" + "bne- 1b" + : "=&b" (i32Result), "=&b" (i32Tmp) + : "r" (pi32Target) : "cc", "memory"); + + return( i32Result - 1); + } + + /********************************************************************** + Desc: + **********************************************************************/ + FINLINE FLMINT32 ftkAtomicExchange( + volatile FLMINT32 * pi32Target, + FLMINT32 i32NewVal) + { + FLMINT32 i32Tmp = 0; + + __asm__ __volatile__ ("\n1:\n\t" + "lwarx %0, 0, %2\n\t" + "stwcx. %3, 0, %2\n\t" + "bne 1b" + : "=r" (i32Tmp) : "0" (i32Tmp), "b" (pi32Target), + "r" (i32NewVal) : "cc", "memory"); + + return( i32Tmp); + } + + #elif defined( __ia64__) + + /********************************************************************** + Desc: + **********************************************************************/ + FINLINE FLMINT32 ia64_compare_and_swap( + volatile FLMINT32 * pi32Target, + FLMINT32 i32NewVal, + FLMINT32 i32CompVal) + { + FLMINT32 i32Old; + + asm volatile ("mov ar.ccv = %2 ;;\n\t" + "cmpxchg4.acq %0 = [%1], %3, ar.ccv\n\t" + : "=r" (i32Old) : "r" (pi32Target), "r" (i32CompVal), + "r" (i32NewVal)); + + return( i32Old); + } - #define ftkAtomicDecrement( puiTarget) \ - ((FLMUINT32)nlm_AtomicDecrement( puiTarget)) + /********************************************************************** + Desc: + **********************************************************************/ + FINLINE FLMINT32 ftkAtomicIncrement( + volatile FLMINT32 * pi32Target) + { + FLMINT32 i32Old; - #define ATOMIC_INCDEC_SUPPORT 1 + for( ;;) + { + i32Old = *pi32Target; + + if( ia64_compare_and_swap( pi32Target, + i32Old + 1, i32Old) == i32Old) + { + break; + } + } + + return( i32Old + 1); + } -#elif defined( FLM_UNIX) + /********************************************************************** + Desc: + **********************************************************************/ + FINLINE FLMINT32 ftkAtomicDecrement( + volatile FLMINT32 * pi32Target) + { + FLMINT32 i32Old; - #if defined(__GNUC__) && defined(__i386__) - #define ATOMIC_INCDEC_SUPPORT 1 + for( ;;) + { + i32Old = *pi32Target; + + if( ia64_compare_and_swap( pi32Target, + i32Old - 1, i32Old) == i32Old) + { + break; + } + } + + return( i32Old - 1); + } + + /********************************************************************** + Desc: + **********************************************************************/ + FINLINE FLMINT32 ftkAtomicExchange( + volatile FLMINT32 * pi32Target, + FLMINT32 i32NewVal) + { + FLMINT32 i32Result; + + for( ;;) + { + i32Result = *pi32Target; + + if( ia64_compare_and_swap( pi32Target, + i32NewVal, i32Result) == i32Result) + { + break; + } + } + + return( iResult); + } + + #elif defined( __s390__) + + /********************************************************************** + Desc: + **********************************************************************/ + FINLINE FLMINT32 ftkAtomicIncrement( + volatile FLMINT32 * pi32Target) + { + FLMINT32 i32Tmp; + + __asm__ __volatile__ ("\tLA\t2,%1\n" + "0:\tL\t%0,%1\n" + "\tLR\t1,%0\n" + "\tAHI\t1,1\n" + "\tCS\t%0,1,0(2)\n" + "\tJNZ\t0b\n" + "\tLR\t%0,1" + : "=r" (i32Tmp), "+m" (*pi32Target) + : : "1", "2", "cc"); + + return( i32Tmp); + } + + /********************************************************************** + Desc: + **********************************************************************/ + FINLINE FLMINT32 ftkAtomicDecrement( + volatile FLMINT32 * pi32Target) + { + FLMINT32 i32Tmp; + + __asm__ __volatile__ ("\tLA\t2,%1\n" + "0:\tL\t%0,%1\n" + "\tLR\t1,%0\n" + "\tAHI\t1,-1\n" + "\tCS\t%0,1,0(2)\n" + "\tJNZ\t0b\n" + "\tLR\t%0,1" + : "=r" (i32Tmp), "+m" (*pi32Target) + : : "1", "2", "cc"); + + return( i32Tmp); + } + + /********************************************************************** + Desc: + **********************************************************************/ + FINLINE FLMINT32 ftkAtomicExchange( + volatile FLMINT32 * pi32Target, + FLMINT32 i32NewVal) + { + FLMINT32 i32Ret; + + __asm__ __volatile__ ("\tLA\t1,%0\n" + "0:\tL\t%1,%0\n" + "\tCS\t%1,%2,0(1)\n" + "\tJNZ\t0b" + : "+m" (*pi32Target), "=r" (i32Ret) + : "r" (i32NewVal) + : "1", "cc"); + + return( i32Ret); + } + + #else + + #error Atomic operations aren't supported on this platform + #endif + +#elif defined( FLM_SPARC) + + /************************************************************************* + Desc: + *************************************************************************/ + FINLINE void begin_spin( + volatile unsigned char * lock) + { + asm( "1: ldstub [%i0], %l0"); + asm( "cmp %l0,0"); + asm( "bne 1b"); + asm( "nop"); + } + + #define BEGIN_SPIN(tmp,lock) \ + begin_spin( &lock); + + #define END_SPIN(lock) \ + ((lock) = 0); + + #define FLM_USE_SPIN_LOCK_ATOMICS + +#else + + #error Atomic operations aren't supported on this platform + +#endif + +#if defined( FLM_USE_SPIN_LOCK_ATOMICS) || defined( FLM_USE_MUTEX_ATOMICS) + + #if FLM_USE_SPIN_LOCK_ATOMICS + #if defined( FLM_SPARC) + extern volatile unsigned char gv_flmAtomicLock; + #endif + #endif + + /************************************************************************* + Desc: + *************************************************************************/ + FINLINE FLMINT32 ftkAtomicIncrement( + volatile FLMINT32 * pi32Target) + { + int iTmp; + FLMINT32 i32RetVal; + + #ifdef FLM_USE_SPIN_LOCK_ATOMICS + BEGIN_SPIN( iTmp, gv_flmAtomicLock) + #else + pthread_mutex_lock( &gv_flmAtomicLock); + #endif + + (*pi32Target)++; + i32RetVal = *pi32Target; + + #ifdef FLM_USE_SPIN_LOCK_ATOMICS + END_SPIN( gv_flmAtomicLock) + #else + pthread_mutex_unlock( &gv_flmAtomicLock); + #endif + + return( i32RetVal); + } + + /************************************************************************* + Desc: + *************************************************************************/ + FINLINE FLMINT32 ftkAtomicDecrement( + volatile FLMINT32 * pi32Target) + { + int iTmp; + FLMINT32 i32RetVal; + + #ifdef FLM_USE_SPIN_LOCK_ATOMICS + BEGIN_SPIN( iTmp, gv_flmAtomicLock) + #else + pthread_mutex_lock( &gv_flmAtomicLock); + #endif + + (*pi32Target)--; + i32RetVal = *pi32Target; + + #ifdef FLM_USE_SPIN_LOCK_ATOMICS + END_SPIN( gv_flmAtomicLock) + #else + pthread_mutex_unlock( &gv_flmAtomicLock); + #endif + + return( i32RetVal); + } + + /************************************************************************* + Desc: + *************************************************************************/ + FINLINE FLMINT32 ftkAtomicExchange( + volatile FLMINT32 * pi32Target, + FLMINT32 i32NewVal) + { + int iTmp; + FLMINT32 i32RetVal; + + #ifdef FLM_USE_SPIN_LOCK_ATOMICS + BEGIN_SPIN( iTmp, gv_flmAtomicLock) + #else + pthread_mutex_lock( &gv_flmAtomicLock); + #endif + + i32RetVal = *pi32Target; + *pi32Target = i32NewVal; + + #ifdef FLM_USE_SPIN_LOCK_ATOMICS + END_SPIN( gv_flmAtomicLock) + #else + pthread_mutex_unlock( &gv_flmAtomicLock); + #endif + + return( i32RetVal); + } #endif @@ -1893,10 +2379,8 @@ FINLINE FLMBYTE f_getHexVal( } /**************************************************************************** - Process ID Functions - -****************************************************************************/ + ****************************************************************************/ #if defined( FLM_WIN) @@ -1971,10 +2455,8 @@ void flmQSortUINTSwap( FLMUINT uiPos2); /**************************************************************************** - Module Load/Unload Functions - -****************************************************************************/ + ****************************************************************************/ typedef void * FlmModHandle; diff --git a/flaim/src/ftkmisc.cpp b/flaim/src/ftkmisc.cpp index 25ae99a..e28b4ff 100644 --- a/flaim/src/ftkmisc.cpp +++ b/flaim/src/ftkmisc.cpp @@ -1,784 +1,784 @@ -//------------------------------------------------------------------------- -// Desc: Miscellaneous toolkit functions. -// Tabs: 3 -// -// Copyright (c) 2000-2003,2005-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: ftkmisc.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ -//------------------------------------------------------------------------- - -#include "flaimsys.h" - -FLMUINT gv_uiSerialInitCount = 0; -f_randomGenerator gv_uiSerialRandom; -F_MUTEX gv_hSerialMutex = F_MUTEX_NULL; - -#if defined( FLM_NLM) - - #pragma pack(push,1) - - extern "C" - { - LONG gv_lMyModuleHandle = 0; - LONG gv_lFlmTimerTag = 0; - LONG gv_lAllocRTag = 0; - static f_randomGenerator gv_flmRandGenerator; - static SEMAPHORE gv_lFlmRandSemaphore = F_SEM_NULL; - static LONG gv_lFlmStartTicks = 0; - static FLMUINT32 gv_ui32NetWareStartupCount = 0; - } - - #pragma pack(pop) - - void f_sleep( - FLMUINT uiMilliseconds) - { - if( ! uiMilliseconds ) - { - kYieldThread(); - } - else - { - kDelayThread( uiMilliseconds); - } - } - -#endif - - -#if defined( FLM_UNIX) - - #ifdef FLM_AIX - #ifndef nsleep - extern "C" - { - extern int nsleep( struct timestruc_t *, struct timestruc_t *); - } - #endif - #endif - -/**************************************************************************** -Desc: This routine causes the calling process to delay the given number - of milliseconds. Due to the nature of the call, the actual sleep - time is almost guaranteed to be different from requested sleep time. -In: milliseconds - the number of milliseconds to delay -****************************************************************************/ -void f_sleep( - FLMUINT uiMilliseconds) -{ -#ifdef FLM_AIX - struct timestruc_t timeout; - struct timestruc_t remain; -#else - struct timespec timeout; -#endif - - timeout.tv_sec = (uiMilliseconds / 1000); - timeout.tv_nsec = (uiMilliseconds % 1000) * 1000000; - -#ifdef FLM_AIX - nsleep(&timeout, &remain); -#else - nanosleep(&timeout, 0); -#endif -} -#endif - -#ifdef FLM_UNIX -/*************************************************************************** -Desc: Map POSIX errno to Flaim IO errors. -***************************************************************************/ -RCODE MapErrnoToFlaimErr( - int err, - RCODE defaultRc) -{ - /* Switch on passed in error code value */ - - switch (err) - { - case 0: - return( FERR_OK); - - case ENOENT: - return( RC_SET( FERR_IO_PATH_NOT_FOUND)); - - case EACCES: - case EEXIST: - return( RC_SET( FERR_IO_ACCESS_DENIED)); - - case EINVAL: - flmAssert( 0); - return( RC_SET( FERR_INVALID_PARM)); - - case EIO: - return( RC_SET( FERR_IO_DISK_FULL)); - - case ENOTDIR: - return( RC_SET( FERR_IO_MODIFY_ERR)); - -#ifdef EBADFD - case EBADFD: - return( RC_SET( FERR_IO_BAD_FILE_HANDLE)); -#endif - - case EOF: - return( RC_SET( FERR_IO_END_OF_FILE)); - - case EMFILE: - return( RC_SET( FERR_IO_NO_MORE_FILES)); - - default: - return( RC_SET( defaultRc)); - } -} -#endif - -/**************************************************************************** -Desc: This routine initializes the serial number generator. If the O/S - does not provide support for GUID generation or if the GUID - routines fail for some reason, a pseudo-GUID will be generated. -Notes: This routine should only be called once by the process. -****************************************************************************/ -RCODE f_initSerialNumberGenerator( void) -{ - FLMUINT uiTime; - RCODE rc = FERR_OK; - - if (++gv_uiSerialInitCount > 1) - { - goto Exit; - } - - if( RC_BAD( rc = f_mutexCreate( &gv_hSerialMutex))) - { - goto Exit; - } - - f_timeGetSeconds( &uiTime ); - -#if defined( FLM_WIN) - f_randomSetSeed( &gv_uiSerialRandom, - (FLMUINT32)(uiTime ^ (FLMUINT)_getpid())); -#elif defined( FLM_NLM) - f_randomSetSeed( &gv_uiSerialRandom, - (FLMUINT32)(uiTime ^ (FLMUINT)GetRunningProcess())); -#elif defined( FLM_UNIX) - f_randomSetSeed( &gv_uiSerialRandom, - (FLMUINT32)(uiTime ^ (FLMUINT)getpid())); -#else - f_randomSetSeed( &gv_uiSerialRandom, (FLMUINT32)uiTime); -#endif - -Exit: - - return( rc); -} - -/**************************************************************************** -Desc: This routine will use the operating system calls to generate a - "globally unique" identifier. Typically, this is based on the - MAC address of an ethernet card installed in the machine. If the - machine does not have an ethernet card, or if the OS does not - support generating GUIDs, this routine will generate a pseudo-GUID - using a random number generator. A serial number is 16-bytes. -****************************************************************************/ -RCODE f_createSerialNumber( - FLMBYTE * pszSerialNum) -{ - RCODE rc = FERR_OK; - -#if defined( FLM_WIN) - - UUID uuidVal; - RPC_STATUS err = UuidCreate( &uuidVal); - - if (err == RPC_S_OK || err == RPC_S_UUID_LOCAL_ONLY) - { - UD2FBA( (FLMUINT32)uuidVal.Data1, &pszSerialNum[ 0]); - UW2FBA( (FLMUINT16)uuidVal.Data2, &pszSerialNum[ 4]); - UW2FBA( (FLMUINT16)uuidVal.Data3, &pszSerialNum[ 6]); - f_memcpy( &pszSerialNum[ 8], (FLMBYTE *)uuidVal.Data4, 8); - goto Exit; - } - -#elif defined( FLM_NLM) - - NWGUID guidVal; - int err = SGUIDCreate( &guidVal); - - if( !err || err == 1) // NOTE: 1 == SGUID_WARN_RANDOM_NODE - { - UD2FBA( guidVal.time_low, &pszSerialNum[ 0]); - UW2FBA( guidVal.time_mid, &pszSerialNum[ 4]); - UW2FBA( guidVal.time_hi_and_version, &pszSerialNum[ 6]); - pszSerialNum[ 8] = guidVal.clk_seq_hi_res; - pszSerialNum[ 9] = guidVal.clk_seq_low; - f_memcpy( &pszSerialNum[ 10], (FLMBYTE *)guidVal.node, 6); - goto Exit; - } - -#endif - - /* - Generate a pseudo GUID value - */ - - flmAssert( gv_hSerialMutex != F_MUTEX_NULL); - - f_mutexLock( gv_hSerialMutex); - - UD2FBA( (FLMUINT32)f_randomLong( - &gv_uiSerialRandom), &pszSerialNum[ 0]); - UD2FBA( (FLMUINT32)f_randomLong( - &gv_uiSerialRandom), &pszSerialNum[ 4]); - UD2FBA( (FLMUINT32)f_randomLong( - &gv_uiSerialRandom), &pszSerialNum[ 8]); - UD2FBA( (FLMUINT32)f_randomLong( - &gv_uiSerialRandom), &pszSerialNum[ 12]); - - f_mutexUnlock( gv_hSerialMutex); - -#if defined( FLM_WIN) || defined( FLM_NLM) -Exit: -#endif - - return( rc); -} - -/**************************************************************************** -Notes: This routine should only be called once by the process. -****************************************************************************/ -void f_freeSerialNumberGenerator( void) -{ - if( (--gv_uiSerialInitCount) > 0) - { - return; - } - - if( gv_hSerialMutex != F_MUTEX_NULL) - { - f_mutexDestroy( &gv_hSerialMutex); - } -} - - -/**************************************************************************** -Desc: Generates a table of remainders for each 8-bit byte. The resulting - table is used by flmUpdateCRC to calculate a CRC value. The table - must be freed via a call to f_freeCRCTable. -*****************************************************************************/ -RCODE f_initCRCTable( - FLMUINT32 ** ppui32CRCTbl) -{ - FLMUINT32 * pTable; - FLMUINT32 ui32Val; - FLMUINT32 ui32Loop; - FLMUINT32 ui32SubLoop; - RCODE rc = FERR_OK; - - // Use the standard degree-32 polynomial used by - // Ethernet, PKZIP, etc. for computing the CRC of - // a data stream. This is the little-endian - // representation of the polynomial. The big-endian - // representation is 0x04C11DB7. - -#define CRC_POLYNOMIAL ((FLMUINT32)0xEDB88320) - - *ppui32CRCTbl = NULL; - - if( RC_BAD( rc = f_alloc( 256 * sizeof( FLMUINT32), &pTable))) - { - goto Exit; - } - - for( ui32Loop = 0; ui32Loop < 256; ui32Loop++) - { - ui32Val = ui32Loop; - for( ui32SubLoop = 0; ui32SubLoop < 8; ui32SubLoop++) - { - if( ui32Val & 0x00000001) - { - ui32Val = CRC_POLYNOMIAL ^ (ui32Val >> 1); - } - else - { - ui32Val >>= 1; - } - } - - pTable[ ui32Loop] = ui32Val; - } - - *ppui32CRCTbl = pTable; - pTable = NULL; - -Exit: - - if( pTable) - { - f_free( &pTable); - } - - return( rc); -} - -/**************************************************************************** -Desc: Computes the CRC of the passed-in data buffer. Multiple calls can - be made to this routine to build a CRC over multiple data buffers. - On the first call, *pui32CRC must be initialized to something - (0, etc.). For generating CRCs that are compatible with PKZIP, - *pui32CRC should be initialized to 0xFFFFFFFF and the ones complement - of the resulting CRC should be computed. -*****************************************************************************/ -void f_updateCRC( - FLMUINT32 * pui32CRCTbl, - FLMBYTE * pucBlk, - FLMUINT uiBlkSize, - FLMUINT32 * pui32CRC) -{ - FLMUINT32 ui32CRC = *pui32CRC; - FLMUINT uiLoop; - - for( uiLoop = 0; uiLoop < uiBlkSize; uiLoop++) - { - ui32CRC = (ui32CRC >> 8) ^ pui32CRCTbl[ - ((FLMBYTE)(ui32CRC & 0x000000FF)) ^ pucBlk[ uiLoop]]; - } - - *pui32CRC = ui32CRC; -} - -/**************************************************************************** -Desc: -****************************************************************************/ -FLMUINT f_breakpoint( - FLMUINT uiBreakFlag) -{ - if( uiBreakFlag) - { -#ifdef FLM_NLM - EnterDebugger(); -#else - flmAssert( 0); -#endif - } - - return( 0); -} - -/**************************************************************************** -Desc: Function that must be called within a NLM's startup routine. -****************************************************************************/ -#ifdef FLM_NLM -RCODE f_netwareStartup( void) -{ - RCODE rc = FERR_OK; - - if( ftkAtomicIncrement( &gv_ui32NetWareStartupCount) != 1) - { - goto Exit; - } - - gv_lMyModuleHandle = CFindLoadModuleHandle( (void *)f_netwareShutdown); - - // Allocate the needed resource tags - - if( (gv_lAllocRTag = AllocateResourceTag( - gv_lMyModuleHandle, - (BYTE *)"NOVDB Memory", AllocSignature)) == NULL) - { - rc = RC_SET( FERR_MEM); - goto Exit; - } - - if( (gv_lFlmTimerTag = AllocateResourceTag( - gv_lMyModuleHandle, - (BYTE *)"NOVDB Timer", TimerSignature)) == NULL) - { - rc = RC_SET( FERR_MEM); - goto Exit; - } - - gv_lFlmStartTicks = GetCurrentTime(); - - // Random Generator initialization - - if( (gv_lFlmRandSemaphore = kSemaphoreAlloc( - (BYTE *)"NOVDB", 1)) == F_SEM_NULL) - { - rc = RC_SET( FERR_MEM); - goto Exit; - } - - f_randomSetSeed( &gv_flmRandGenerator, 1); - -Exit: - - if( RC_BAD( rc)) - { - f_netwareShutdown(); - } - - return( rc); -} -#endif - -/**************************************************************************** -Desc: Closes (Frees) any resources used by FLAIM's clib patches layer. -****************************************************************************/ -#ifdef FLM_NLM -void f_netwareShutdown( void) -{ - // Call exit function. - - if( ftkAtomicDecrement( &gv_ui32NetWareStartupCount) != 0) - { - goto Exit; - } - - if( gv_lAllocRTag) - { - ReturnResourceTag( gv_lAllocRTag, 1); - gv_lAllocRTag = 0; - } - - if( gv_lFlmTimerTag) - { - ReturnResourceTag( gv_lFlmTimerTag, 1); - gv_lFlmTimerTag = 0; - } - - if( gv_lFlmRandSemaphore) - { - kSemaphoreFree( gv_lFlmRandSemaphore); - gv_lFlmRandSemaphore = 0; - } - - gv_lFlmStartTicks = 0; - gv_lMyModuleHandle = 0; - -Exit: - - return; -} -#endif - -/**************************************************************************** -Desc: -****************************************************************************/ -#ifdef FLM_NLM -FLMUINT f_getNLMHandle( void) -{ - return( (FLMUINT)gv_lMyModuleHandle); -} -#endif - -/**************************************************************************** -Desc: This routine is required to work around known bugs or inefficiencies - in various incarnations of clib. -****************************************************************************/ -#undef f_memset -void * f_memset( - void * pvMem, - FLMBYTE ucByte, - FLMUINT uiSize) -{ -#ifndef FLM_NLM - return( memset( pvMem, ucByte, uiSize)); -#else - char * cp = (char *)pvMem; - unsigned dwordLength; - unsigned long dwordVal; - - dwordVal = ((unsigned long)ucByte << 24) | - ((unsigned long)ucByte << 16) | - ((unsigned long)ucByte << 8) | - (unsigned long)ucByte; - - while( uiSize && ((long)cp & 3L)) - { - *cp++ = (char)ucByte; - uiSize--; - } - - dwordLength = uiSize >> 2; - if( dwordLength != 0) - { - CSetD( dwordVal, (void *)cp, dwordLength); - cp += (dwordLength << 2); - uiSize -= (dwordLength << 2); - } - - while( uiSize) - { - *cp++ = (char)ucByte; - uiSize--; - } - - return( pvMem); -#endif -} - -/**************************************************************************** -Desc: This routine is required to work around known bugs or inefficiencies - in various incarnations of clib. -****************************************************************************/ -#undef f_memmove -void * f_memmove( - void * pvDest, - const void * pvSrc, - FLMUINT uiSize) -{ -#ifndef FLM_NLM - return( memmove( pvDest, pvSrc, uiSize)); -#else -#define CMOVB_THRESHOLD 16 - char *s = (char *)pvSrc; - char *d = (char *)pvDest; - unsigned uDiff; - - if( (char *)(s + uiSize) < d || (char *)(d + uiSize) < s) - { - // The source and destination do not overlap. - - CMoveFast( (void *)s, d, (LONG)uiSize); - } - else if( s < d) - { - // Source preceeds the destination, with overlap. - - uDiff = (unsigned)(d - s); - d += uiSize; - s += uiSize; - if( uDiff >= CMOVB_THRESHOLD) - { - for( ;;) - { - if( uiSize < uDiff) - { - break; - } - - // Copy the tail - - s -= uDiff; - d -= uDiff; - uiSize -= uDiff; - CMoveFast( (void *)s, d, (LONG)uDiff); - } - } - - // Copy remaining bytes. - - while( uiSize--) - { - *--d = *--s; - } - } - else if( s > d) - { - // Source follows the destination, with overlap. - - uDiff = (unsigned)(s - d); - if( uDiff >= CMOVB_THRESHOLD) - { - for( ;;) - { - if( uiSize < uDiff) - { - break; - } - - // Copy the head - - CMoveFast( (void *)s, d, (LONG)uDiff); - uiSize -= uDiff; - d += uDiff; - s += uDiff; - } - } - - // Copy the remaining bytes - - while( uiSize--) - { - *d++ = *s++; - } - } - - // Else, the regions overlap completely (s == d). Do nothing. - - return( pvDest); -#endif -} - -/**************************************************************************** -Desc: Performs a comparison of m1 to m2, for a maximum - length of size bytes. -****************************************************************************/ -#undef f_memcmp -FLMINT f_memcmp( - const void * pvMem1, - const void * pvMem2, - FLMUINT uiSize) -{ - unsigned char * s1; - unsigned char * s2; - - for (s1 = (unsigned char *)pvMem1, s2 = (unsigned char *)pvMem2; - uiSize > 0; uiSize--, s1++, s2++) - { - if (*s1 == *s2) - { - continue; - } - else if( *s1 > *s2) - { - return( 1); - } - else - { - return( -1); - } - } - - return( 0); -} - -/**************************************************************************** -Desc: This routine is required to work around known bugs or inefficiencies - in various incarnations of clib. -****************************************************************************/ -#undef f_stricmp -FLMINT f_stricmp( - const char * pszStr1, - const char * pszStr2) -{ - while( f_toupper( *pszStr1) == f_toupper( *pszStr2) && *pszStr1) - { - pszStr1++; - pszStr2++; - } - return( (FLMINT)( f_toupper( *pszStr1) - f_toupper( *pszStr2))); -} - -/**************************************************************************** -Desc: Performs a signed comparison of s1 to s2, for a maximum - length of n bytes, starting with the first character in - each string and continuing with subsequent characters until - the corresponding characters differ, or until n characters - have been examined. -****************************************************************************/ -#undef f_strnicmp -FLMINT f_strnicmp( - const char * pszStr1, - const char * pszStr2, - FLMINT iLen) -{ - if( !pszStr1 || !pszStr2) - { - return( (pszStr1 == pszStr2) - ? 0 - : (pszStr1 ? 1 : -1)); - } - - while( iLen-- && *pszStr1 && *pszStr2 && - (f_toupper( *pszStr1) == f_toupper( *pszStr2))) - { - pszStr1++; - pszStr2++; - } - - return( (iLen == -1) - ? 0 - : (f_toupper( *pszStr1) - f_toupper( *pszStr2))); - -} - -/**************************************************************************** -Desc: -****************************************************************************/ -#undef f_strupr -char * f_strupr( - char * pszStr) -{ - while( *pszStr) - { - *pszStr = f_toupper( *pszStr); - pszStr++; - } - - return( pszStr); -} - -/**************************************************************************** -Desc: -****************************************************************************/ -#undef f_strstr -char * f_strstr( - const char * pszStr1, - const char * pszStr2) -{ - FLMUINT i; - FLMUINT j; - FLMUINT k; - - if ( !pszStr1 || !pszStr2) - { - return( NULL); - } - - for( i = 0; pszStr1[i] != '\0'; i++) - { - for( j=i, k=0; pszStr2[k] != '\0' && - pszStr1[j] == pszStr2[k]; j++, k++) - { - ; - } - - if ( k > 0 && pszStr2[k] == '\0') - { - return( (char *)&pszStr1[i]); - } - } - - return( NULL); -} - -/**************************************************************************** -Desc: -****************************************************************************/ -#undef f_strchr -char * f_strchr( - const char * pszStr, - char c) -{ - if( !pszStr) - { - return( NULL); - } - - while (*pszStr && *pszStr != (FLMBYTE)c) - { - pszStr++; - } - - return( (char *)((*pszStr == c) - ? pszStr - : NULL)); -} +//------------------------------------------------------------------------- +// Desc: Miscellaneous toolkit functions. +// Tabs: 3 +// +// Copyright (c) 2000-2003,2005-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: ftkmisc.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +FLMUINT gv_uiSerialInitCount = 0; +f_randomGenerator gv_uiSerialRandom; +F_MUTEX gv_hSerialMutex = F_MUTEX_NULL; + +#if defined( FLM_NLM) + + #pragma pack(push,1) + + extern "C" + { + LONG gv_lMyModuleHandle = 0; + LONG gv_lFlmTimerTag = 0; + LONG gv_lAllocRTag = 0; + static f_randomGenerator gv_flmRandGenerator; + static SEMAPHORE gv_lFlmRandSemaphore = F_SEM_NULL; + static LONG gv_lFlmStartTicks = 0; + static FLMUINT32 gv_ui32NetWareStartupCount = 0; + } + + #pragma pack(pop) + + void f_sleep( + FLMUINT uiMilliseconds) + { + if( ! uiMilliseconds ) + { + kYieldThread(); + } + else + { + kDelayThread( uiMilliseconds); + } + } + +#endif + + +#if defined( FLM_UNIX) + + #ifdef FLM_AIX + #ifndef nsleep + extern "C" + { + extern int nsleep( struct timestruc_t *, struct timestruc_t *); + } + #endif + #endif + +/**************************************************************************** +Desc: This routine causes the calling process to delay the given number + of milliseconds. Due to the nature of the call, the actual sleep + time is almost guaranteed to be different from requested sleep time. +In: milliseconds - the number of milliseconds to delay +****************************************************************************/ +void f_sleep( + FLMUINT uiMilliseconds) +{ +#ifdef FLM_AIX + struct timestruc_t timeout; + struct timestruc_t remain; +#else + struct timespec timeout; +#endif + + timeout.tv_sec = (uiMilliseconds / 1000); + timeout.tv_nsec = (uiMilliseconds % 1000) * 1000000; + +#ifdef FLM_AIX + nsleep(&timeout, &remain); +#else + nanosleep(&timeout, 0); +#endif +} +#endif + +#ifdef FLM_UNIX +/*************************************************************************** +Desc: Map POSIX errno to Flaim IO errors. +***************************************************************************/ +RCODE MapErrnoToFlaimErr( + int err, + RCODE defaultRc) +{ + /* Switch on passed in error code value */ + + switch (err) + { + case 0: + return( FERR_OK); + + case ENOENT: + return( RC_SET( FERR_IO_PATH_NOT_FOUND)); + + case EACCES: + case EEXIST: + return( RC_SET( FERR_IO_ACCESS_DENIED)); + + case EINVAL: + flmAssert( 0); + return( RC_SET( FERR_INVALID_PARM)); + + case EIO: + return( RC_SET( FERR_IO_DISK_FULL)); + + case ENOTDIR: + return( RC_SET( FERR_IO_MODIFY_ERR)); + +#ifdef EBADFD + case EBADFD: + return( RC_SET( FERR_IO_BAD_FILE_HANDLE)); +#endif + + case EOF: + return( RC_SET( FERR_IO_END_OF_FILE)); + + case EMFILE: + return( RC_SET( FERR_IO_NO_MORE_FILES)); + + default: + return( RC_SET( defaultRc)); + } +} +#endif + +/**************************************************************************** +Desc: This routine initializes the serial number generator. If the O/S + does not provide support for GUID generation or if the GUID + routines fail for some reason, a pseudo-GUID will be generated. +Notes: This routine should only be called once by the process. +****************************************************************************/ +RCODE f_initSerialNumberGenerator( void) +{ + FLMUINT uiTime; + RCODE rc = FERR_OK; + + if (++gv_uiSerialInitCount > 1) + { + goto Exit; + } + + if( RC_BAD( rc = f_mutexCreate( &gv_hSerialMutex))) + { + goto Exit; + } + + f_timeGetSeconds( &uiTime ); + +#if defined( FLM_WIN) + f_randomSetSeed( &gv_uiSerialRandom, + (FLMUINT32)(uiTime ^ (FLMUINT)_getpid())); +#elif defined( FLM_NLM) + f_randomSetSeed( &gv_uiSerialRandom, + (FLMUINT32)(uiTime ^ (FLMUINT)GetRunningProcess())); +#elif defined( FLM_UNIX) + f_randomSetSeed( &gv_uiSerialRandom, + (FLMUINT32)(uiTime ^ (FLMUINT)getpid())); +#else + f_randomSetSeed( &gv_uiSerialRandom, (FLMUINT32)uiTime); +#endif + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: This routine will use the operating system calls to generate a + "globally unique" identifier. Typically, this is based on the + MAC address of an ethernet card installed in the machine. If the + machine does not have an ethernet card, or if the OS does not + support generating GUIDs, this routine will generate a pseudo-GUID + using a random number generator. A serial number is 16-bytes. +****************************************************************************/ +RCODE f_createSerialNumber( + FLMBYTE * pszSerialNum) +{ + RCODE rc = FERR_OK; + +#if defined( FLM_WIN) + + UUID uuidVal; + RPC_STATUS err = UuidCreate( &uuidVal); + + if (err == RPC_S_OK || err == RPC_S_UUID_LOCAL_ONLY) + { + UD2FBA( (FLMUINT32)uuidVal.Data1, &pszSerialNum[ 0]); + UW2FBA( (FLMUINT16)uuidVal.Data2, &pszSerialNum[ 4]); + UW2FBA( (FLMUINT16)uuidVal.Data3, &pszSerialNum[ 6]); + f_memcpy( &pszSerialNum[ 8], (FLMBYTE *)uuidVal.Data4, 8); + goto Exit; + } + +#elif defined( FLM_NLM) + + NWGUID guidVal; + int err = SGUIDCreate( &guidVal); + + if( !err || err == 1) // NOTE: 1 == SGUID_WARN_RANDOM_NODE + { + UD2FBA( guidVal.time_low, &pszSerialNum[ 0]); + UW2FBA( guidVal.time_mid, &pszSerialNum[ 4]); + UW2FBA( guidVal.time_hi_and_version, &pszSerialNum[ 6]); + pszSerialNum[ 8] = guidVal.clk_seq_hi_res; + pszSerialNum[ 9] = guidVal.clk_seq_low; + f_memcpy( &pszSerialNum[ 10], (FLMBYTE *)guidVal.node, 6); + goto Exit; + } + +#endif + + /* + Generate a pseudo GUID value + */ + + flmAssert( gv_hSerialMutex != F_MUTEX_NULL); + + f_mutexLock( gv_hSerialMutex); + + UD2FBA( (FLMUINT32)f_randomLong( + &gv_uiSerialRandom), &pszSerialNum[ 0]); + UD2FBA( (FLMUINT32)f_randomLong( + &gv_uiSerialRandom), &pszSerialNum[ 4]); + UD2FBA( (FLMUINT32)f_randomLong( + &gv_uiSerialRandom), &pszSerialNum[ 8]); + UD2FBA( (FLMUINT32)f_randomLong( + &gv_uiSerialRandom), &pszSerialNum[ 12]); + + f_mutexUnlock( gv_hSerialMutex); + +#if defined( FLM_WIN) || defined( FLM_NLM) +Exit: +#endif + + return( rc); +} + +/**************************************************************************** +Notes: This routine should only be called once by the process. +****************************************************************************/ +void f_freeSerialNumberGenerator( void) +{ + if( (--gv_uiSerialInitCount) > 0) + { + return; + } + + if( gv_hSerialMutex != F_MUTEX_NULL) + { + f_mutexDestroy( &gv_hSerialMutex); + } +} + + +/**************************************************************************** +Desc: Generates a table of remainders for each 8-bit byte. The resulting + table is used by flmUpdateCRC to calculate a CRC value. The table + must be freed via a call to f_freeCRCTable. +*****************************************************************************/ +RCODE f_initCRCTable( + FLMUINT32 ** ppui32CRCTbl) +{ + FLMUINT32 * pTable; + FLMUINT32 ui32Val; + FLMUINT32 ui32Loop; + FLMUINT32 ui32SubLoop; + RCODE rc = FERR_OK; + + // Use the standard degree-32 polynomial used by + // Ethernet, PKZIP, etc. for computing the CRC of + // a data stream. This is the little-endian + // representation of the polynomial. The big-endian + // representation is 0x04C11DB7. + +#define CRC_POLYNOMIAL ((FLMUINT32)0xEDB88320) + + *ppui32CRCTbl = NULL; + + if( RC_BAD( rc = f_alloc( 256 * sizeof( FLMUINT32), &pTable))) + { + goto Exit; + } + + for( ui32Loop = 0; ui32Loop < 256; ui32Loop++) + { + ui32Val = ui32Loop; + for( ui32SubLoop = 0; ui32SubLoop < 8; ui32SubLoop++) + { + if( ui32Val & 0x00000001) + { + ui32Val = CRC_POLYNOMIAL ^ (ui32Val >> 1); + } + else + { + ui32Val >>= 1; + } + } + + pTable[ ui32Loop] = ui32Val; + } + + *ppui32CRCTbl = pTable; + pTable = NULL; + +Exit: + + if( pTable) + { + f_free( &pTable); + } + + return( rc); +} + +/**************************************************************************** +Desc: Computes the CRC of the passed-in data buffer. Multiple calls can + be made to this routine to build a CRC over multiple data buffers. + On the first call, *pui32CRC must be initialized to something + (0, etc.). For generating CRCs that are compatible with PKZIP, + *pui32CRC should be initialized to 0xFFFFFFFF and the ones complement + of the resulting CRC should be computed. +*****************************************************************************/ +void f_updateCRC( + FLMUINT32 * pui32CRCTbl, + FLMBYTE * pucBlk, + FLMUINT uiBlkSize, + FLMUINT32 * pui32CRC) +{ + FLMUINT32 ui32CRC = *pui32CRC; + FLMUINT uiLoop; + + for( uiLoop = 0; uiLoop < uiBlkSize; uiLoop++) + { + ui32CRC = (ui32CRC >> 8) ^ pui32CRCTbl[ + ((FLMBYTE)(ui32CRC & 0x000000FF)) ^ pucBlk[ uiLoop]]; + } + + *pui32CRC = ui32CRC; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMUINT f_breakpoint( + FLMUINT uiBreakFlag) +{ + if( uiBreakFlag) + { +#ifdef FLM_NLM + EnterDebugger(); +#else + flmAssert( 0); +#endif + } + + return( 0); +} + +/**************************************************************************** +Desc: Function that must be called within a NLM's startup routine. +****************************************************************************/ +#ifdef FLM_NLM +RCODE f_netwareStartup( void) +{ + RCODE rc = FERR_OK; + + if( ftkAtomicIncrement( &gv_i32NetWareStartupCount) != 1) + { + goto Exit; + } + + gv_lMyModuleHandle = CFindLoadModuleHandle( (void *)f_netwareShutdown); + + // Allocate the needed resource tags + + if( (gv_lAllocRTag = AllocateResourceTag( + gv_lMyModuleHandle, + (BYTE *)"NOVDB Memory", AllocSignature)) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( (gv_lFlmTimerTag = AllocateResourceTag( + gv_lMyModuleHandle, + (BYTE *)"NOVDB Timer", TimerSignature)) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + gv_lFlmStartTicks = GetCurrentTime(); + + // Random Generator initialization + + if( (gv_lFlmRandSemaphore = kSemaphoreAlloc( + (BYTE *)"NOVDB", 1)) == F_SEM_NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + f_randomSetSeed( &gv_flmRandGenerator, 1); + +Exit: + + if( RC_BAD( rc)) + { + f_netwareShutdown(); + } + + return( rc); +} +#endif + +/**************************************************************************** +Desc: Closes (Frees) any resources used by FLAIM's clib patches layer. +****************************************************************************/ +#ifdef FLM_NLM +void f_netwareShutdown( void) +{ + // Call exit function. + + if( ftkAtomicDecrement( &gv_ui32NetWareStartupCount) != 0) + { + goto Exit; + } + + if( gv_lAllocRTag) + { + ReturnResourceTag( gv_lAllocRTag, 1); + gv_lAllocRTag = 0; + } + + if( gv_lFlmTimerTag) + { + ReturnResourceTag( gv_lFlmTimerTag, 1); + gv_lFlmTimerTag = 0; + } + + if( gv_lFlmRandSemaphore) + { + kSemaphoreFree( gv_lFlmRandSemaphore); + gv_lFlmRandSemaphore = 0; + } + + gv_lFlmStartTicks = 0; + gv_lMyModuleHandle = 0; + +Exit: + + return; +} +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +#ifdef FLM_NLM +FLMUINT f_getNLMHandle( void) +{ + return( (FLMUINT)gv_lMyModuleHandle); +} +#endif + +/**************************************************************************** +Desc: This routine is required to work around known bugs or inefficiencies + in various incarnations of clib. +****************************************************************************/ +#undef f_memset +void * f_memset( + void * pvMem, + FLMBYTE ucByte, + FLMUINT uiSize) +{ +#ifndef FLM_NLM + return( memset( pvMem, ucByte, uiSize)); +#else + char * cp = (char *)pvMem; + unsigned dwordLength; + unsigned long dwordVal; + + dwordVal = ((unsigned long)ucByte << 24) | + ((unsigned long)ucByte << 16) | + ((unsigned long)ucByte << 8) | + (unsigned long)ucByte; + + while( uiSize && ((long)cp & 3L)) + { + *cp++ = (char)ucByte; + uiSize--; + } + + dwordLength = uiSize >> 2; + if( dwordLength != 0) + { + CSetD( dwordVal, (void *)cp, dwordLength); + cp += (dwordLength << 2); + uiSize -= (dwordLength << 2); + } + + while( uiSize) + { + *cp++ = (char)ucByte; + uiSize--; + } + + return( pvMem); +#endif +} + +/**************************************************************************** +Desc: This routine is required to work around known bugs or inefficiencies + in various incarnations of clib. +****************************************************************************/ +#undef f_memmove +void * f_memmove( + void * pvDest, + const void * pvSrc, + FLMUINT uiSize) +{ +#ifndef FLM_NLM + return( memmove( pvDest, pvSrc, uiSize)); +#else +#define CMOVB_THRESHOLD 16 + char *s = (char *)pvSrc; + char *d = (char *)pvDest; + unsigned uDiff; + + if( (char *)(s + uiSize) < d || (char *)(d + uiSize) < s) + { + // The source and destination do not overlap. + + CMoveFast( (void *)s, d, (LONG)uiSize); + } + else if( s < d) + { + // Source preceeds the destination, with overlap. + + uDiff = (unsigned)(d - s); + d += uiSize; + s += uiSize; + if( uDiff >= CMOVB_THRESHOLD) + { + for( ;;) + { + if( uiSize < uDiff) + { + break; + } + + // Copy the tail + + s -= uDiff; + d -= uDiff; + uiSize -= uDiff; + CMoveFast( (void *)s, d, (LONG)uDiff); + } + } + + // Copy remaining bytes. + + while( uiSize--) + { + *--d = *--s; + } + } + else if( s > d) + { + // Source follows the destination, with overlap. + + uDiff = (unsigned)(s - d); + if( uDiff >= CMOVB_THRESHOLD) + { + for( ;;) + { + if( uiSize < uDiff) + { + break; + } + + // Copy the head + + CMoveFast( (void *)s, d, (LONG)uDiff); + uiSize -= uDiff; + d += uDiff; + s += uDiff; + } + } + + // Copy the remaining bytes + + while( uiSize--) + { + *d++ = *s++; + } + } + + // Else, the regions overlap completely (s == d). Do nothing. + + return( pvDest); +#endif +} + +/**************************************************************************** +Desc: Performs a comparison of m1 to m2, for a maximum + length of size bytes. +****************************************************************************/ +#undef f_memcmp +FLMINT f_memcmp( + const void * pvMem1, + const void * pvMem2, + FLMUINT uiSize) +{ + unsigned char * s1; + unsigned char * s2; + + for (s1 = (unsigned char *)pvMem1, s2 = (unsigned char *)pvMem2; + uiSize > 0; uiSize--, s1++, s2++) + { + if (*s1 == *s2) + { + continue; + } + else if( *s1 > *s2) + { + return( 1); + } + else + { + return( -1); + } + } + + return( 0); +} + +/**************************************************************************** +Desc: This routine is required to work around known bugs or inefficiencies + in various incarnations of clib. +****************************************************************************/ +#undef f_stricmp +FLMINT f_stricmp( + const char * pszStr1, + const char * pszStr2) +{ + while( f_toupper( *pszStr1) == f_toupper( *pszStr2) && *pszStr1) + { + pszStr1++; + pszStr2++; + } + return( (FLMINT)( f_toupper( *pszStr1) - f_toupper( *pszStr2))); +} + +/**************************************************************************** +Desc: Performs a signed comparison of s1 to s2, for a maximum + length of n bytes, starting with the first character in + each string and continuing with subsequent characters until + the corresponding characters differ, or until n characters + have been examined. +****************************************************************************/ +#undef f_strnicmp +FLMINT f_strnicmp( + const char * pszStr1, + const char * pszStr2, + FLMINT iLen) +{ + if( !pszStr1 || !pszStr2) + { + return( (pszStr1 == pszStr2) + ? 0 + : (pszStr1 ? 1 : -1)); + } + + while( iLen-- && *pszStr1 && *pszStr2 && + (f_toupper( *pszStr1) == f_toupper( *pszStr2))) + { + pszStr1++; + pszStr2++; + } + + return( (iLen == -1) + ? 0 + : (f_toupper( *pszStr1) - f_toupper( *pszStr2))); + +} + +/**************************************************************************** +Desc: +****************************************************************************/ +#undef f_strupr +char * f_strupr( + char * pszStr) +{ + while( *pszStr) + { + *pszStr = f_toupper( *pszStr); + pszStr++; + } + + return( pszStr); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +#undef f_strstr +char * f_strstr( + const char * pszStr1, + const char * pszStr2) +{ + FLMUINT i; + FLMUINT j; + FLMUINT k; + + if ( !pszStr1 || !pszStr2) + { + return( NULL); + } + + for( i = 0; pszStr1[i] != '\0'; i++) + { + for( j=i, k=0; pszStr2[k] != '\0' && + pszStr1[j] == pszStr2[k]; j++, k++) + { + ; + } + + if ( k > 0 && pszStr2[k] == '\0') + { + return( (char *)&pszStr1[i]); + } + } + + return( NULL); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +#undef f_strchr +char * f_strchr( + const char * pszStr, + char c) +{ + if( !pszStr) + { + return( NULL); + } + + while (*pszStr && *pszStr != (FLMBYTE)c) + { + pszStr++; + } + + return( (char *)((*pszStr == c) + ? pszStr + : NULL)); +} diff --git a/flaim/src/ftkthrd.cpp b/flaim/src/ftkthrd.cpp index fb385ef..d37abda 100644 --- a/flaim/src/ftkthrd.cpp +++ b/flaim/src/ftkthrd.cpp @@ -46,53 +46,53 @@ /**************************************************************************** Desc: Add a Reference to this object. ****************************************************************************/ -FLMUINT F_Thread::AddRef( +FLMINT F_Thread::AddRef( FLMBOOL bMutexLocked) { - FLMUINT uiRefCnt; + FLMINT iRefCnt; if( !bMutexLocked) { f_mutexLock( m_hMutex); } - uiRefCnt = ++m_ui32RefCnt; + iRefCnt = ++m_i32RefCnt; if( !bMutexLocked) { f_mutexUnlock( m_hMutex); } - return( uiRefCnt); + return( iRefCnt); } /**************************************************************************** Desc: Removes a reference to this object. ****************************************************************************/ -FLMUINT F_Thread::Release( +FLMINT F_Thread::Release( FLMBOOL bMutexLocked) { - FLMUINT uiRefCnt; + FLMINT iRefCnt; if( !bMutexLocked && m_hMutex != F_MUTEX_NULL) { f_mutexLock( m_hMutex); } - flmAssert( m_ui32RefCnt > 0); - uiRefCnt = --m_ui32RefCnt; + flmAssert( m_i32RefCnt > 0); + iRefCnt = --m_i32RefCnt; if( !bMutexLocked && m_hMutex != F_MUTEX_NULL) { f_mutexUnlock( m_hMutex); } - if( !uiRefCnt) + if( !iRefCnt) { delete this; } - return( uiRefCnt); + return( iRefCnt); } /**************************************************************************** diff --git a/flaim/src/ftkthrd.h b/flaim/src/ftkthrd.h index 888ab64..4f8ee44 100644 --- a/flaim/src/ftkthrd.h +++ b/flaim/src/ftkthrd.h @@ -201,18 +201,18 @@ public: cleanupThread(); } - FLMUINT AddRef( + FLMINT AddRef( FLMBOOL bMutexLocked); - FINLINE FLMUINT AddRef( void) + FINLINE FLMINT AddRef( void) { return( AddRef( FALSE)); } - FLMUINT Release( + FLMINT Release( FLMBOOL bMutexLocked); - FINLINE FLMUINT Release( void) + FINLINE FLMINT Release( void) { return( Release( FALSE)); } diff --git a/flaim/src/ftrace.cpp b/flaim/src/ftrace.cpp index d5d3de1..dad1736 100644 --- a/flaim/src/ftrace.cpp +++ b/flaim/src/ftrace.cpp @@ -63,34 +63,34 @@ FlmTrace::~FlmTrace() Public: addRef Desc: Add a reference to this object. ****************************************************************************/ -FLMUINT FlmTrace::AddRef( void) +FLMINT FlmTrace::AddRef( void) { - FLMUINT uiReturnCnt; + FLMINT iRefCnt; lock(); - uiReturnCnt = ++m_ui32RefCnt; + iRefCnt = ++m_i32RefCnt; unlock(); - return uiReturnCnt; + return( iRefCnt); } /**************************************************************************** Public: release Desc: Removes a reference to this object. ****************************************************************************/ -FLMUINT FlmTrace::Release( void) +FLMINT FlmTrace::Release( void) { - FLMUINT uiReturnCnt; + FLMINT iRefCnt; lock(); - uiReturnCnt = --m_ui32RefCnt; + iRefCnt = --m_i32RefCnt; unlock(); - if( !uiReturnCnt) + if( !iRefCnt) { delete this; } - return uiReturnCnt; + return( iRefCnt); } /**************************************************************************** diff --git a/flaim/src/ftrace.h b/flaim/src/ftrace.h index 9d7f152..21041b5 100644 --- a/flaim/src/ftrace.h +++ b/flaim/src/ftrace.h @@ -107,8 +107,9 @@ public: virtual ~FlmTrace(); - FLMUINT AddRef( void); - FLMUINT Release( void); + FLMINT AddRef( void); + + FLMINT Release( void); FINLINE void enableCategory( FLMUINT uiCategory) diff --git a/flaim/src/imonfact.cpp b/flaim/src/imonfact.cpp index 976b5e8..b58db34 100644 --- a/flaim/src/imonfact.cpp +++ b/flaim/src/imonfact.cpp @@ -147,9 +147,6 @@ Exit: /**************************************************************************** Desc: Tells the factory that the WebPage object is no longer needed ****************************************************************************/ -// For now, we're going to keep Release() pretty simple. In the near -// future, we may want to implement some sort of caching scheme for -// the WebPage objects... void F_WebPageFactory::Release( F_WebPage ** ppPage) { diff --git a/flaim/src/imonfhdl.cpp b/flaim/src/imonfhdl.cpp index 637a2f1..2b8906c 100644 --- a/flaim/src/imonfhdl.cpp +++ b/flaim/src/imonfhdl.cpp @@ -567,13 +567,13 @@ void F_FileHdlPage::write_data( // Now for the final base class - F_Base pBase = (F_Base *)pListItem; - // m_ui32RefCnt - printHTMLUint( - "F_Base.m_ui32RefCnt", - "FLMUINT", + // m_i32RefCnt + printHTMLInt( + "F_Base.m_i32RefCnt", + "FLMINT32", (void *)pBase, - (void *)&pBase->m_ui32RefCnt, - pBase->m_ui32RefCnt, + (void *)&pBase->m_i32RefCnt, + pBase->m_i32RefCnt, (bHighlight = ~bHighlight)); diff --git a/flaim/src/imonfmgr.cpp b/flaim/src/imonfmgr.cpp index dc1cf5f..e4b2dd5 100644 --- a/flaim/src/imonfmgr.cpp +++ b/flaim/src/imonfmgr.cpp @@ -248,13 +248,13 @@ RCODE F_FileHdlMgrPage::display( pBase = (F_Base *)pFileHdlMgr; - // m_ui32RefCnt - printHTMLUint( - "F_Base.m_ui32RefCnt", - "FLMUINT", + // m_i32RefCnt + printHTMLInt( + "F_Base.m_i32RefCnt", + "FLMINT32", (void *)pBase, - (void *)&pBase->m_ui32RefCnt, - pBase->m_ui32RefCnt, + (void *)&pBase->m_i32RefCnt, + pBase->m_i32RefCnt, (bHighlight = ~bHighlight)); diff --git a/flaim/src/rcache.cpp b/flaim/src/rcache.cpp index 500b0fd..3157506 100644 --- a/flaim/src/rcache.cpp +++ b/flaim/src/rcache.cpp @@ -1,3025 +1,3018 @@ -//------------------------------------------------------------------------- -// Desc: Record caching -// Tabs: 3 -// -// Copyright (c) 1999-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: rcache.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ -//------------------------------------------------------------------------- - -#include "flaimsys.h" - -#if defined( FLM_NLM) && !defined( __MWERKS__) -// Disable "Warning! W549: col(XX) 'sizeof' operand contains -// compiler generated information" - #pragma warning 549 9 -#endif - -FSTATIC FLMBOOL rcaCanRelocate( - void * pvAlloc); - -FSTATIC void rcaRelocate( - void * pvOldAlloc, - void * pvNewAlloc); - -// Extended record object for accessing private members of FlmRecord - -struct FlmRecordExt -{ - static FINLINE void clearCached( - FlmRecord * pRec) - { - pRec->clearCached(); - } - - static FINLINE void setCached( - FlmRecord * pRec) - { - pRec->setCached(); - } - - static FINLINE void setReadOnly( - FlmRecord * pRec) - { - pRec->setReadOnly(); - } - - static FINLINE FLMUINT AddRef( - FlmRecord * pRec, - FLMBOOL bMutexLocked) - { - return( pRec->AddRef( bMutexLocked)); - } - - static FINLINE FLMUINT Release( - FlmRecord * pRec, - FLMBOOL bMutexLocked) - { - return( pRec->Release( bMutexLocked)); - } - - static FINLINE void setOldVersion( - FlmRecord * pRec) - { - pRec->setOldVersion(); - } - - static FINLINE void clearOldVersion( - FlmRecord * pRec) - { - pRec->clearOldVersion(); - } - - static FINLINE FLMUINT getFlags( - FlmRecord * pRec) - { - return( pRec->m_uiFlags); - } - - static FLMBOOL canRelocateRec( - void * pvAlloc); - - static void relocateRec( - void * pvOldAlloc, - void * pvNewAlloc); - - static FLMBOOL canRelocateRecBuffer( - void * pvAlloc); - - static void relocateRecBuffer( - void * pvOldAlloc, - void * pvNewAlloc); -}; - -// Functions for calculating minimum and maximum record counts for a -// given hash table size. - -#define FLM_RCA_MIN_REC_CNT(uiHashTblSz) ((uiHashTblSz) / 4) -#define FLM_RCA_MAX_REC_CNT(uiHashTblSz) ((uiHashTblSz) * 4) - -// Hash function for hashing to records in record cache. - -#define FLM_RCA_HASH( uiDrn) \ - (RCACHE **)(&(gv_FlmSysData.RCacheMgr.ppHashBuckets[(uiDrn) & \ - (gv_FlmSysData.RCacheMgr.uiHashMask)])) - -/* LOCAL STATIC FUNCTION PROTOTYPES */ - -FSTATIC void flmRcaFreePurged( - RCACHE * pRCache); - -FSTATIC void flmRcaFreeCache( - RCACHE * pRCache, - FLMBOOL bPutInPurgeList); - -FSTATIC FLMUINT flmRcaGetBestHashTblSize( - FLMUINT uiCurrRecCount); - -FSTATIC RCODE flmRcaRehash( void); - -FSTATIC RCODE flmRcaSetMemLimit( - FLMUINT uiMaxCacheBytes); - -FSTATIC RCODE flmRcaWaitNotify( - FNOTIFY ** ppNotifyListRV); - -FSTATIC void flmRcaNotify( - FNOTIFY * pNotify, - RCACHE * pUseRCache, - RCODE NotifyRc); - -FSTATIC RCODE flmRcaAllocCacheStruct( - RCACHE ** ppRCache); - -FSTATIC void flmRcaFreeCacheStruct( - RCACHE ** ppRCache); - -FSTATIC void flmRcaSetRecord( - RCACHE * pRCache, - FlmRecord * pNewRecord); - -FSTATIC void flmRcaLinkIntoRCache( - RCACHE * pNewerRCache, - RCACHE * pOlderRCache, - RCACHE * pRCache, - FLMBOOL bLinkAsMRU); - -FSTATIC void flmRcaLinkToFFILE( - RCACHE * pRCache, - FFILE_p pFile, - FDB_p pDb, - FLMUINT uiLowTransId, - FLMBOOL bMostCurrent); - -#ifdef FLM_DEBUG -FSTATIC RCODE flmRcaCheck( - FDB_p pDb, - FLMUINT uiContainer, - FLMUINT uiDrn); -#endif - -/**************************************************************************** -Desc: This inline assumes that the global mutex is locked, because - it potentially updates the cache usage statistics. -****************************************************************************/ -FINLINE void flmRcaSetTransID( - RCACHE * pRCache, - FLMUINT uiNewTransID) -{ - if (pRCache->uiHighTransId == 0xFFFFFFFF && - uiNewTransID != 0xFFFFFFFF) - { - FLMUINT uiSize = (FLMUINT)((pRCache->pRecord) - ? pRCache->pRecord->getTotalMemory() - : (FLMUINT)0) + sizeof( RCACHE); - gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes += uiSize; - gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount++; - - if( pRCache->pRecord) - { - FlmRecordExt::setOldVersion( pRCache->pRecord); - } - } - else if (pRCache->uiHighTransId != 0xFFFFFFFF && - uiNewTransID == 0xFFFFFFFF) - { - FLMUINT uiSize = (FLMUINT)((pRCache->pRecord) - ? pRCache->pRecord->getTotalMemory() - : (FLMUINT)0) + sizeof( RCACHE); - flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes >= uiSize); - flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount); - gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes -= uiSize; - gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount--; - if( pRCache->pRecord) - { - FlmRecordExt::clearOldVersion( pRCache->pRecord); - } - } - pRCache->uiHighTransId = uiNewTransID; -} - -/**************************************************************************** -Desc: This routine links a record into the global list as the MRU record. - This routine assumes that the record cache mutex has already - been locked. -****************************************************************************/ -FINLINE void flmRcaLinkToGlobalAsMRU( - RCACHE * pRCache) -{ - pRCache->pPrevInGlobal = NULL; - if ((pRCache->pNextInGlobal = gv_FlmSysData.RCacheMgr.pMRURecord) != NULL) - { - gv_FlmSysData.RCacheMgr.pMRURecord->pPrevInGlobal = pRCache; - } - else - { - gv_FlmSysData.RCacheMgr.pLRURecord = pRCache; - } - gv_FlmSysData.RCacheMgr.pMRURecord = pRCache; -} - -/**************************************************************************** -Desc: This routine links a record into the global list as the LRU record. - This routine assumes that the record cache mutex has already - been locked. -****************************************************************************/ -FINLINE void flmRcaLinkToGlobalAsLRU( - RCACHE * pRCache) -{ - pRCache->pNextInGlobal = NULL; - if ((pRCache->pPrevInGlobal = gv_FlmSysData.RCacheMgr.pLRURecord) != NULL) - { - gv_FlmSysData.RCacheMgr.pLRURecord->pNextInGlobal = pRCache; - } - else - { - gv_FlmSysData.RCacheMgr.pMRURecord = pRCache; - } - gv_FlmSysData.RCacheMgr.pLRURecord = pRCache; -} - -/**************************************************************************** -Desc: Moves a record one step closer to the MRU slot in the global list. - This routine assumes that the record cache mutex has already - been locked. -****************************************************************************/ -FINLINE void flmRcaStepUpInGlobalList( - RCACHE * pRCache) -{ - RCACHE * pPrevRCache; - - if( (pPrevRCache = pRCache->pPrevInGlobal) != NULL) - { - if( pPrevRCache->pPrevInGlobal) - { - pPrevRCache->pPrevInGlobal->pNextInGlobal = pRCache; - } - else - { - gv_FlmSysData.RCacheMgr.pMRURecord = pRCache; - } - - pRCache->pPrevInGlobal = pPrevRCache->pPrevInGlobal; - pPrevRCache->pPrevInGlobal = pRCache; - pPrevRCache->pNextInGlobal = pRCache->pNextInGlobal; - - if( pRCache->pNextInGlobal) - { - pRCache->pNextInGlobal->pPrevInGlobal = pPrevRCache; - } - else - { - gv_FlmSysData.RCacheMgr.pLRURecord = pPrevRCache; - } - pRCache->pNextInGlobal = pPrevRCache; - } -} - -/**************************************************************************** -Desc: This routine unlinks a record from the global list This routine - assumes that the record cache mutex has already been locked. -****************************************************************************/ -FINLINE void flmRcaUnlinkFromGlobal( - RCACHE * pRCache) -{ - if (pRCache->pNextInGlobal) - { - pRCache->pNextInGlobal->pPrevInGlobal = pRCache->pPrevInGlobal; - } - else - { - gv_FlmSysData.RCacheMgr.pLRURecord = pRCache->pPrevInGlobal; - } - if (pRCache->pPrevInGlobal) - { - pRCache->pPrevInGlobal->pNextInGlobal = pRCache->pNextInGlobal; - } - else - { - gv_FlmSysData.RCacheMgr.pMRURecord = pRCache->pNextInGlobal; - } - pRCache->pPrevInGlobal = pRCache->pNextInGlobal = NULL; -} - -/**************************************************************************** -Desc: This routine unlinks a record from the global purged list This routine - assumes that the record cache mutex has already been locked. -****************************************************************************/ -FINLINE void flmRcaUnlinkFromPurged( - RCACHE * pRCache) -{ - if (pRCache->pNextInGlobal) - { - pRCache->pNextInGlobal->pPrevInGlobal = pRCache->pPrevInGlobal; - } - - if (pRCache->pPrevInGlobal) - { - pRCache->pPrevInGlobal->pNextInGlobal = pRCache->pNextInGlobal; - } - else - { - gv_FlmSysData.RCacheMgr.pPurgeList = pRCache->pNextInGlobal; - } - - pRCache->pPrevInGlobal = NULL; - pRCache->pNextInGlobal = NULL; -} - -/**************************************************************************** -Desc: -****************************************************************************/ -FINLINE void flmRcaLinkToHeapList( - RCACHE * pRCache) -{ - flmAssert( !pRCache->pPrevInHeapList); - flmAssert( !pRCache->pNextInHeapList); - flmAssert( !RCA_IS_IN_HEAP_LIST( pRCache->uiFlags)); - flmAssert( FlmRecordExt::getFlags( pRCache->pRecord) & RCA_HEAP_BUFFER); - - if( (pRCache->pNextInHeapList = gv_FlmSysData.RCacheMgr.pHeapList) != NULL) - { - pRCache->pNextInHeapList->pPrevInHeapList = pRCache; - } - gv_FlmSysData.RCacheMgr.pHeapList = pRCache; - RCA_SET_IN_HEAP_LIST( pRCache->uiFlags); -} - -/**************************************************************************** -Desc: -****************************************************************************/ -FINLINE void flmRcaUnlinkFromHeapList( - RCACHE * pRCache) -{ - flmAssert( RCA_IS_IN_HEAP_LIST( pRCache->uiFlags)); - - if( pRCache->pNextInHeapList) - { - pRCache->pNextInHeapList->pPrevInHeapList = pRCache->pPrevInHeapList; - } - - if( pRCache->pPrevInHeapList) - { - pRCache->pPrevInHeapList->pNextInHeapList = pRCache->pNextInHeapList; - } - else - { - gv_FlmSysData.RCacheMgr.pHeapList = pRCache->pNextInHeapList; - } - - pRCache->pPrevInHeapList = NULL; - pRCache->pNextInHeapList = NULL; - - RCA_UNSET_IN_HEAP_LIST( pRCache->uiFlags); -} - -/**************************************************************************** -Desc: This routine links a record to an FFILE list at the head of the list. - This routine assumes that the record cache mutex has already been - locked. -****************************************************************************/ -FINLINE void flmRcaLinkToFileAtHead( - RCACHE * pRCache, - FFILE_p pFile) -{ - pRCache->pPrevInFile = NULL; - if ((pRCache->pNextInFile = pFile->pFirstRecord) != NULL) - { - pFile->pFirstRecord->pPrevInFile = pRCache; - } - else - { - pFile->pLastRecord = pRCache; - } - - pFile->pFirstRecord = pRCache; - pRCache->pFile = pFile; - RCA_SET_LINKED_TO_FILE( pRCache->uiFlags); -} - -/**************************************************************************** -Desc: This routine links a record to an FFILE list at the end of the list. - This routine assumes that the record cache mutex has already been - locked. -****************************************************************************/ -FINLINE void flmRcaLinkToFileAtEnd( - RCACHE * pRCache, - FFILE_p pFile) -{ - pRCache->pNextInFile = NULL; - if( (pRCache->pPrevInFile = pFile->pLastRecord) != NULL) - { - pFile->pLastRecord->pNextInFile = pRCache; - } - else - { - pFile->pFirstRecord = pRCache; - } - pFile->pLastRecord = pRCache; - pRCache->pFile = pFile; - RCA_SET_LINKED_TO_FILE( pRCache->uiFlags); -} - -/**************************************************************************** -Desc: This routine unlinks a record from its FFILE list. This routine - assumes that the record cache mutex has already been locked. -****************************************************************************/ -FINLINE void flmRcaUnlinkFromFile( - RCACHE * pRCache) -{ - if( RCA_IS_LINKED_TO_FILE( pRCache->uiFlags)) - { - if( pRCache->pNextInFile) - { - pRCache->pNextInFile->pPrevInFile = pRCache->pPrevInFile; - } - else - { - pRCache->pFile->pLastRecord = pRCache->pPrevInFile; - } - if( pRCache->pPrevInFile) - { - pRCache->pPrevInFile->pNextInFile = pRCache->pNextInFile; - } - else - { - pRCache->pFile->pFirstRecord = pRCache->pNextInFile; - } - pRCache->pPrevInFile = pRCache->pNextInFile = NULL; - RCA_UNSET_LINKED_TO_FILE( pRCache->uiFlags); - } -} - -/**************************************************************************** -Desc: This routine links a record into its hash bucket. This routine - assumes that the record cache mutex has already been locked. -****************************************************************************/ -FINLINE void flmRcaLinkToHashBucket( - RCACHE * pRCache) -{ - RCACHE ** ppHashBucket = FLM_RCA_HASH( pRCache->uiDrn); - - flmAssert( pRCache->pNewerVersion == NULL); - - pRCache->pPrevInBucket = NULL; - if( (pRCache->pNextInBucket = *ppHashBucket) != NULL) - { - pRCache->pNextInBucket->pPrevInBucket = pRCache; - } - *ppHashBucket = pRCache; -} - -/**************************************************************************** -Desc: This routine unlinks a record from its hash bucket. This routine - assumes that the record cache mutex has already been locked. -****************************************************************************/ -FINLINE void flmRcaUnlinkFromHashBucket( - RCACHE * pRCache) -{ - flmAssert( pRCache->pNewerVersion == NULL); - if (pRCache->pNextInBucket) - { - pRCache->pNextInBucket->pPrevInBucket = pRCache->pPrevInBucket; - } - if (pRCache->pPrevInBucket) - { - pRCache->pPrevInBucket->pNextInBucket = pRCache->pNextInBucket; - } - else - { - RCACHE ** ppHashBucket = FLM_RCA_HASH( pRCache->uiDrn); - *ppHashBucket = pRCache->pNextInBucket; - } - pRCache->pPrevInBucket = pRCache->pNextInBucket = NULL; -} - -/**************************************************************************** -Desc: This routine unlinks a record from its version list. This routine - assumes that the record cache mutex has already been locked. -****************************************************************************/ -FINLINE void flmRcaLinkToVerList( - RCACHE * pRCache, - RCACHE * pNewerVer, - RCACHE * pOlderVer) -{ - if( (pRCache->pNewerVersion = pNewerVer) != NULL) - { - pNewerVer->pOlderVersion = pRCache; - } - if ((pRCache->pOlderVersion = pOlderVer) != NULL) - { - pOlderVer->pNewerVersion = pRCache; - } -} - -/**************************************************************************** -Desc: This routine unlinks a record from its version list. This routine - assumes that the record cache mutex has already been locked. -****************************************************************************/ -FINLINE void flmRcaUnlinkFromVerList( - RCACHE * pRCache) -{ - if (pRCache->pNewerVersion) - { - pRCache->pNewerVersion->pOlderVersion = pRCache->pOlderVersion; - } - if (pRCache->pOlderVersion) - { - pRCache->pOlderVersion->pNewerVersion = pRCache->pNewerVersion; - } - pRCache->pNewerVersion = pRCache->pOlderVersion = NULL; -} - -/**************************************************************************** -Desc: This routine frees a purged from record cache. This routine assumes - that the record cache mutex has already been locked. -****************************************************************************/ -FSTATIC void flmRcaFreePurged( - RCACHE * pRCache) -{ - FLMUINT uiTotalMemory; - FLMUINT uiFreeMemory; - - // Release the record data object we are pointing to. - - if (pRCache->pRecord) - { - if( RCA_IS_IN_HEAP_LIST( pRCache->uiFlags)) - { - flmRcaUnlinkFromHeapList( pRCache); - } - - uiTotalMemory = pRCache->pRecord->getTotalMemory(); - uiFreeMemory = pRCache->pRecord->getFreeMemory(); - flmAssert( uiTotalMemory >= uiFreeMemory); - FlmRecordExt::clearCached( pRCache->pRecord); - FlmRecordExt::Release( pRCache->pRecord, TRUE); - pRCache->pRecord = NULL; - } - else - { - uiTotalMemory = 0; - uiFreeMemory = 0; - } - - // If this is an old version, decrement the old version counters. - - if (pRCache->uiHighTransId != 0xFFFFFFFF) - { - FLMUINT uiTotalOldMemory = uiTotalMemory + sizeof( RCACHE); - - flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes >= - uiTotalOldMemory); - flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount); - gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes -= uiTotalOldMemory; - gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount--; - } - - // Unlink the RCACHE from the purged list. - - flmRcaUnlinkFromPurged( pRCache); - - // Free the RCACHE structure. - - RCA_UNSET_PURGED( pRCache->uiFlags); - flmRcaFreeCacheStruct( &pRCache); -} - -/**************************************************************************** -Desc: This routine frees a record in the record cache. This routine assumes - that the record cache mutex has already been locked. -****************************************************************************/ -FSTATIC void flmRcaFreeCache( - RCACHE * pRCache, - FLMBOOL bPutInPurgeList) -{ - FLMUINT uiTotalMemory; - FLMUINT uiFreeMemory; - FLMBOOL bOldVersion; -#ifdef FLM_DBG_LOG - char szTmpBuf[ 80]; -#endif - - // Release the record data object we are pointing to. - - if (pRCache->pRecord && !bPutInPurgeList) - { - if( RCA_IS_IN_HEAP_LIST( pRCache->uiFlags)) - { - flmRcaUnlinkFromHeapList( pRCache); - } - - uiTotalMemory = pRCache->pRecord->getTotalMemory(); - uiFreeMemory = pRCache->pRecord->getFreeMemory(); - flmAssert( uiTotalMemory >= uiFreeMemory); - FlmRecordExt::clearCached( pRCache->pRecord); - FlmRecordExt::Release( pRCache->pRecord, TRUE); - pRCache->pRecord = NULL; - } - else - { - uiTotalMemory = 0; - uiFreeMemory = 0; - } - bOldVersion = (FLMBOOL)((pRCache->uiHighTransId != 0xFFFFFFFF) - ? TRUE - : FALSE); - -#ifdef FLM_DBG_LOG - f_sprintf( szTmpBuf, "RCD:H%X", - (unsigned)pRCache->uiHighTransId); - - flmDbgLogWrite( pRCache->pFile ? pRCache->pFile->uiFFileId : 0, pRCache->uiContainer, - pRCache->uiDrn, pRCache->uiLowTransId, szTmpBuf); -#endif - - // Unlink the RCACHE from its various lists. - - flmRcaUnlinkFromGlobal( pRCache); - flmRcaUnlinkFromFile( pRCache); - if (!pRCache->pNewerVersion) - { - RCACHE * pOlderVersion = pRCache->pOlderVersion; - - flmRcaUnlinkFromHashBucket( pRCache); - - // If there was an older version, it now needs to be - // put into the hash bucket. - - if (pOlderVersion) - { - flmRcaUnlinkFromVerList( pRCache); - flmRcaLinkToHashBucket( pOlderVersion); - } - } - else - { - flmRcaUnlinkFromVerList( pRCache); - } - - // Free the RCACHE structure if not putting in purge list. - - if (!bPutInPurgeList) - { - // If this was an older version, decrement the old version counters. - - if (bOldVersion) - { - FLMUINT uiTotalOldMemory = uiTotalMemory + sizeof( RCACHE); - - flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes >= - uiTotalOldMemory); - flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount); - gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount--; - gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes -= - uiTotalOldMemory; - } - flmRcaFreeCacheStruct( &pRCache); - } - else - { - if ((pRCache->pNextInGlobal = gv_FlmSysData.RCacheMgr.pPurgeList) != NULL) - { - pRCache->pNextInGlobal->pPrevInGlobal = pRCache; - } - gv_FlmSysData.RCacheMgr.pPurgeList = pRCache; - RCA_SET_PURGED( pRCache->uiFlags); - } -} - -/**************************************************************************** -Desc: This routine initializes record cache manager. -****************************************************************************/ -RCODE flmRcaInit( - FLMUINT uiMaxRecordCacheBytes) -{ - RCODE rc = FERR_OK; - - f_memset( &gv_FlmSysData.RCacheMgr, 0, sizeof( RCACHE_MGR)); - gv_FlmSysData.RCacheMgr.Usage.uiMaxBytes = uiMaxRecordCacheBytes; - gv_FlmSysData.RCacheMgr.hMutex = F_MUTEX_NULL; - - // Allocate the hash buckets. - - if (RC_BAD( rc = f_calloc( - (FLMUINT)sizeof( RCACHE *) * - (FLMUINT)MIN_RCACHE_BUCKETS, - &gv_FlmSysData.RCacheMgr.ppHashBuckets))) - { - goto Exit; - } - gv_FlmSysData.RCacheMgr.uiNumBuckets = MIN_RCACHE_BUCKETS; - gv_FlmSysData.RCacheMgr.uiHashMask = - gv_FlmSysData.RCacheMgr.uiNumBuckets - 1; - gv_FlmSysData.RCacheMgr.Usage.uiTotalBytesAllocated += - (sizeof( RCACHE *) * gv_FlmSysData.RCacheMgr.uiNumBuckets); - - // Allocate the mutex for controlling access to the - // record cache. - - if (RC_BAD( rc = f_mutexCreate( &gv_FlmSysData.RCacheMgr.hMutex))) - { - goto Exit; - } - - // Set up the RCACHE struct allocator - - if( (gv_FlmSysData.RCacheMgr.pRCacheAlloc = f_new F_FixedAlloc) == NULL) - { - rc = RC_SET( FERR_MEM); - goto Exit; - } - - if( RC_BAD( rc = gv_FlmSysData.RCacheMgr.pRCacheAlloc->setup( - gv_FlmSysData.pSlabManager, TRUE, sizeof( RCACHE), - &gv_FlmSysData.RCacheMgr.Usage.uiTotalBytesAllocated))) - { - goto Exit; - } - - gv_FlmSysData.RCacheMgr.pRCacheAlloc->setRelocationFuncs( - rcaCanRelocate, rcaRelocate); - - // Set up the record object allocator - - if( (gv_FlmSysData.RCacheMgr.pRecAlloc = f_new F_FixedAlloc) == NULL) - { - rc = RC_SET( FERR_MEM); - goto Exit; - } - - if( RC_BAD( rc = gv_FlmSysData.RCacheMgr.pRecAlloc->setup( - gv_FlmSysData.pSlabManager, - gv_FlmSysData.RCacheMgr.pRCacheAlloc->getMutex(), - sizeof( FlmRecord), - &gv_FlmSysData.RCacheMgr.Usage.uiTotalBytesAllocated))) - { - goto Exit; - } - - gv_FlmSysData.RCacheMgr.pRecAlloc->setRelocationFuncs( - FlmRecordExt::canRelocateRec, FlmRecordExt::relocateRec); - - // Set up the record buffer allocator - - if( (gv_FlmSysData.RCacheMgr.pRecBufAlloc = f_new F_BufferAlloc) == NULL) - { - rc = RC_SET( FERR_MEM); - goto Exit; - } - - if( RC_BAD( rc = gv_FlmSysData.RCacheMgr.pRecBufAlloc->setup( - gv_FlmSysData.pSlabManager, - gv_FlmSysData.RCacheMgr.pRCacheAlloc->getMutex(), - &gv_FlmSysData.RCacheMgr.Usage.uiTotalBytesAllocated))) - { - goto Exit; - } - - gv_FlmSysData.RCacheMgr.pRecBufAlloc->setRelocationFuncs( - FlmRecordExt::canRelocateRecBuffer, - FlmRecordExt::relocateRecBuffer); - -#ifdef FLM_DEBUG - gv_FlmSysData.RCacheMgr.bDebug = TRUE; -#endif - -Exit: - if (RC_BAD( rc)) - { - flmRcaExit(); - } - - return( rc); -} - -/**************************************************************************** -Desc: This routine determines what hash table size best fits the current - record count. It finds the hash bucket size whose midpoint between - the minimum and maximum range is closest to the record count. -****************************************************************************/ -FSTATIC FLMUINT flmRcaGetBestHashTblSize( - FLMUINT uiCurrRecCount) -{ - FLMUINT uiHashTblSize; - FLMUINT uiMaxRecsForHashTblSize; - FLMUINT uiMinRecsForHashTblSize; - FLMUINT uiClosestHashTblSize = 0; - FLMUINT uiDistanceFromMidpoint; - FLMUINT uiLowestDistanceFromMidpoint; - FLMUINT uiHashTblRecsMidpoint; - - uiLowestDistanceFromMidpoint = 0xFFFFFFFF; - for (uiHashTblSize = MIN_RCACHE_BUCKETS; - uiHashTblSize <= MAX_RCACHE_BUCKETS; - uiHashTblSize *= 2) - { - - // Maximum desirable record count for a specific hash table size - // we have arbitrarily chosen to be four times the number of buckets. - // Minimum desirable record count we have arbitrarily chosen to be - // the hash table size divided by four. - - uiMaxRecsForHashTblSize = FLM_RCA_MAX_REC_CNT( uiHashTblSize); - uiMinRecsForHashTblSize = FLM_RCA_MIN_REC_CNT( uiHashTblSize); - - // Ignore any hash bucket sizes where the current record count - // is not between the desired minimum and maximum. - - if (uiCurrRecCount >= uiMinRecsForHashTblSize && - uiCurrRecCount <= uiMaxRecsForHashTblSize) - { - - // Calculate the midpoint between the minimum and maximum - // for this particular hash table size. - - uiHashTblRecsMidpoint = (uiMaxRecsForHashTblSize - - uiMinRecsForHashTblSize) / 2; - - // See how far our current record count is from this midpoint. - - uiDistanceFromMidpoint = (FLMUINT)((uiHashTblRecsMidpoint > uiCurrRecCount) - ? (uiHashTblRecsMidpoint - uiCurrRecCount) - : (uiCurrRecCount - uiHashTblRecsMidpoint)); - - // If the distance from the midpoint is closer than our previous - // lowest distance, save it. - - if (uiDistanceFromMidpoint < uiLowestDistanceFromMidpoint) - { - uiClosestHashTblSize = uiHashTblSize; - uiLowestDistanceFromMidpoint = uiDistanceFromMidpoint; - } - } - } - - // Take the number of buckets whose middle was closest to the - // current record count; - - if (uiLowestDistanceFromMidpoint == 0xFFFFFFFF) - { - // If we did not fall between any of the minimums or maximums, - // we are either below the lowest minimum, or higher than the - // highest maximum. - - uiHashTblSize = (FLMUINT)((uiCurrRecCount < FLM_RCA_MIN_REC_CNT( MIN_RCACHE_BUCKETS)) - ? (FLMUINT)MIN_RCACHE_BUCKETS - : (FLMUINT)MAX_RCACHE_BUCKETS); - - } - else - { - uiHashTblSize = uiClosestHashTblSize; - } - return( uiHashTblSize); -} - -/**************************************************************************** -Desc: This routine resizes the hash table for the record cache manager. - NOTE: This routine assumes that the record cache mutex has been locked. -****************************************************************************/ -FSTATIC RCODE flmRcaRehash( void) -{ - RCODE rc = FERR_OK; - FLMUINT uiNewHashTblSize; - RCACHE ** ppOldHashTbl; - FLMUINT uiOldHashTblSize; - RCACHE ** ppBucket; - FLMUINT uiLoop; - RCACHE * pTmpRCache; - RCACHE * pTmpNextRCache; - - uiNewHashTblSize = flmRcaGetBestHashTblSize( - gv_FlmSysData.RCacheMgr.Usage.uiCount); - - // At this point we better have a different hash table size - // or something is mucked up! - - flmAssert( uiNewHashTblSize != - gv_FlmSysData.RCacheMgr.uiNumBuckets); - - // Save the old hash table and its size. - - ppOldHashTbl = gv_FlmSysData.RCacheMgr.ppHashBuckets; - uiOldHashTblSize = gv_FlmSysData.RCacheMgr.uiNumBuckets; - - // Allocate a new hash table. - - if (RC_BAD( rc = f_calloc( - (FLMUINT)sizeof( RCACHE *) * - (FLMUINT)uiNewHashTblSize, - &gv_FlmSysData.RCacheMgr.ppHashBuckets))) - { - gv_FlmSysData.RCacheMgr.ppHashBuckets = ppOldHashTbl; - goto Exit; - } - - // Subtract off old size and add in new size. - - gv_FlmSysData.RCacheMgr.pRCacheAlloc->decrementTotalBytesAllocated( - (sizeof( RCACHE *) * uiOldHashTblSize)); - gv_FlmSysData.RCacheMgr.pRCacheAlloc->incrementTotalBytesAllocated( - (sizeof( RCACHE *) * uiNewHashTblSize)); - - gv_FlmSysData.RCacheMgr.uiNumBuckets = uiNewHashTblSize; - gv_FlmSysData.RCacheMgr.uiHashMask = uiNewHashTblSize - 1; - - // Relink all of the records into the new - // hash table. - - for (uiLoop = 0, ppBucket = ppOldHashTbl; - uiLoop < uiOldHashTblSize; - uiLoop++, ppBucket++) - { - pTmpRCache = *ppBucket; - while (pTmpRCache) - { - pTmpNextRCache = pTmpRCache->pNextInBucket; - flmRcaLinkToHashBucket( pTmpRCache); - pTmpRCache = pTmpNextRCache; - } - } - - // Throw away the old hash table. - - f_free( &ppOldHashTbl); - -Exit: - - return( rc); -} - -/**************************************************************************** -Desc: This routine changes the cache size for the record cache manager. - If necessary, it will resize the hash table. NOTE: This routine - assumes that the record cache mutex has been locked. -****************************************************************************/ -FSTATIC RCODE flmRcaSetMemLimit( - FLMUINT uiMaxCacheBytes) -{ - RCODE rc = FERR_OK; - - // If we are shrinking the maximum cache, clean up and - // defragment cache first - - gv_FlmSysData.RCacheMgr.Usage.uiMaxBytes = uiMaxCacheBytes; - if (gv_FlmSysData.RCacheMgr.pRCacheAlloc->getTotalBytesAllocated() > - uiMaxCacheBytes) - { - flmRcaCleanupCache( ~((FLMUINT)0), TRUE); - } - - // If the current record count is below the minimum records for the - // number of buckets or is greater than the maximum records for the - // number of buckets, we want to resize the hash table. - - if ((gv_FlmSysData.RCacheMgr.Usage.uiCount > - FLM_RCA_MAX_REC_CNT( gv_FlmSysData.RCacheMgr.uiNumBuckets) && - gv_FlmSysData.RCacheMgr.uiNumBuckets < MAX_RCACHE_BUCKETS) || - (gv_FlmSysData.RCacheMgr.Usage.uiCount < - FLM_RCA_MIN_REC_CNT( gv_FlmSysData.RCacheMgr.uiNumBuckets) && - gv_FlmSysData.RCacheMgr.uiNumBuckets > MIN_RCACHE_BUCKETS)) - { - if (RC_BAD( rc = flmRcaRehash())) - { - goto Exit; - } - } - -Exit: - - return( rc); -} - -/**************************************************************************** -Desc: This routine configures the record cache manager. NOTE: This routine - assumes that the record cache mutex has been locked. -****************************************************************************/ -RCODE flmRcaConfig( - FLMUINT uiType, - void * Value1, - void * Value2) -{ - RCODE rc = FERR_OK; - - F_UNREFERENCED_PARM( Value2); - - switch (uiType) - { - case FLM_CACHE_LIMIT: - rc = flmRcaSetMemLimit( (FLMUINT)Value1); - break; - case FLM_SCACHE_DEBUG: -#ifdef FLM_DEBUG - gv_FlmSysData.RCacheMgr.bDebug = (FLMBOOL)(Value1 ? - (FLMBOOL)TRUE : (FLMBOOL)FALSE); -#endif - break; - default: - rc = RC_SET( FERR_NOT_IMPLEMENTED); - goto Exit; - } - -Exit: - - return( rc); -} - -/**************************************************************************** -Desc: This routine shuts down the record cache manager and frees all - resources allocated by it. -****************************************************************************/ -void flmRcaExit( void) -{ - if (gv_FlmSysData.RCacheMgr.hMutex != F_MUTEX_NULL) - { - f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); - } - - // Free all of the record cache objects. - - while (gv_FlmSysData.RCacheMgr.pMRURecord) - { - f_yieldCPU(); - flmRcaFreeCache( gv_FlmSysData.RCacheMgr.pMRURecord, FALSE); - } - - // Must free those in the purge list too. - - while (gv_FlmSysData.RCacheMgr.pPurgeList) - { - f_yieldCPU(); - flmRcaFreePurged( gv_FlmSysData.RCacheMgr.pPurgeList); - } - - // The math better be consistent! - - flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiCount == 0); - flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount == 0); - flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes == 0); - - // Free the hash bucket array - - if (gv_FlmSysData.RCacheMgr.ppHashBuckets) - { - f_free( &gv_FlmSysData.RCacheMgr.ppHashBuckets); - gv_FlmSysData.RCacheMgr.pRCacheAlloc->decrementTotalBytesAllocated( - (sizeof( RCACHE *) * gv_FlmSysData.RCacheMgr.uiNumBuckets)); - } - - // Free the mutex that controls access to record cache. - // NOTE: This should be done last so that the mutex is not - // unlocked until the very end. - - if (gv_FlmSysData.RCacheMgr.hMutex != F_MUTEX_NULL) - { - f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); - f_mutexDestroy( &gv_FlmSysData.RCacheMgr.hMutex); - } - - // Free the allocators - - if( gv_FlmSysData.RCacheMgr.pRecBufAlloc) - { - gv_FlmSysData.RCacheMgr.pRecBufAlloc->Release(); - gv_FlmSysData.RCacheMgr.pRecBufAlloc = NULL; - } - - if( gv_FlmSysData.RCacheMgr.pRecAlloc) - { - gv_FlmSysData.RCacheMgr.pRecAlloc->Release(); - gv_FlmSysData.RCacheMgr.pRecAlloc = NULL; - } - - if( gv_FlmSysData.RCacheMgr.pRCacheAlloc) - { - gv_FlmSysData.RCacheMgr.pRCacheAlloc->Release(); - gv_FlmSysData.RCacheMgr.pRCacheAlloc = NULL; - } - - // Zero the entire structure out, just for good measure. - - f_memset( &gv_FlmSysData.RCacheMgr, 0, sizeof( RCACHE_MGR)); -} - -/**************************************************************************** -Desc: This routine links a notify request into a notification list and - then waits to be notified that the event has occurred. - NOTE: This routine assumes that the record cache mutex is locked and that - it is supposed to unlock it. It will relock the mutex on its way out. -****************************************************************************/ -FSTATIC RCODE flmRcaWaitNotify( - FNOTIFY ** ppNotifyListRV) -{ - FNOTIFY * pNotify = NULL; - RCODE TempRc; - RCODE rc = FERR_OK; - F_SEM hSem; - - // First create a notify request and link it into the list. - - if (RC_OK( rc = f_calloc( (FLMUINT)(sizeof( FNOTIFY)), &pNotify))) - { - // Allocate a semaphore for the notify request. - - pNotify->uiThreadId = f_threadId(); - pNotify->hSem = F_SEM_NULL; - rc = f_semCreate( &pNotify->hSem); - } - - if (RC_BAD( rc)) - { - if (pNotify) - { - if (pNotify->hSem != F_SEM_NULL) - { - f_semDestroy( &pNotify->hSem); - } - - f_free( &pNotify); - } - - goto Exit; - } - - pNotify->pRc = &rc; - pNotify->UserData = NULL; - pNotify->pNext = *ppNotifyListRV; - *ppNotifyListRV = pNotify; - hSem = pNotify->hSem; - - // Unlock the mutex and wait on the semaphore. - - f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); - if (RC_BAD( TempRc = f_semWait( hSem, F_SEM_WAITFOREVER))) - { - rc = TempRc; - } - - // Free the semaphore and the notify structure. - - f_semDestroy( &hSem); - f_free( &pNotify); - - // Relock the record cache mutex - - f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); - -Exit: - - return( rc); -} - -/**************************************************************************** -Desc: This routine notifies threads waiting for a pending read to complete. - NOTE: This routine assumes that the record cache mutex is already - locked. -****************************************************************************/ -FSTATIC void flmRcaNotify( - FNOTIFY * pNotify, - RCACHE * pUseRCache, - RCODE NotifyRc) -{ - while (pNotify) - { - F_SEM hSem; - - *(pNotify->pRc) = NotifyRc; - if (RC_OK( NotifyRc)) - { - RCA_INCR_USE_COUNT( pUseRCache->uiFlags); - } - hSem = pNotify->hSem; - pNotify = pNotify->pNext; - f_semSignal( hSem); - } -} - -/**************************************************************************** -Desc: Allocate a new record cache structure. This routine assumes that the - record cache mutex has already been locked. -****************************************************************************/ -FSTATIC RCODE flmRcaAllocCacheStruct( - RCACHE ** ppRCache) -{ - RCODE rc = FERR_OK; - - if( (*ppRCache = - (RCACHE *)gv_FlmSysData.RCacheMgr.pRCacheAlloc->allocCell()) == NULL) - { - rc = RC_SET( FERR_MEM); - goto Exit; - } - - f_memset( *ppRCache, 0, sizeof( RCACHE)); - - // Increment the total records cached - - gv_FlmSysData.RCacheMgr.Usage.uiCount++; - - // Set the high transaction ID to 0xFFFFFFFF so that this will NOT - // be treated as one that had memory assigned to the old records. - - (*ppRCache)->uiHighTransId = 0xFFFFFFFF; - -Exit: - - return( rc); -} - -/**************************************************************************** -Desc: Free a record cache structure. This routine assumes that the record - cache mutex has already been locked. -****************************************************************************/ -FSTATIC void flmRcaFreeCacheStruct( - RCACHE ** ppRCache) -{ - flmAssert( !RCA_IS_IN_HEAP_LIST( (*ppRCache)->uiFlags)); - - gv_FlmSysData.RCacheMgr.pRCacheAlloc->freeCell( *ppRCache); - *ppRCache = NULL; - - flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiCount > 0); - gv_FlmSysData.RCacheMgr.Usage.uiCount--; -} - -/**************************************************************************** -Desc: Cleanup old records in cache that are no longer needed by any - transaction. -****************************************************************************/ -void flmRcaCleanupCache( - FLMUINT uiMaxLockTime, - FLMBOOL bMutexesLocked) -{ - RCACHE * pTmpRCache; - RCACHE * pPrevRCache; - RCACHE * pNextRCache; - FLMUINT uiRecordsExamined = 0; - FLMUINT uiLastTimePaused = FLM_GET_TIMER(); - FLMUINT uiCurrTime; - FLMBOOL bUnlockMutexes = FALSE; - - if( !bMutexesLocked) - { - f_mutexLock( gv_FlmSysData.hShareMutex); - f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); - bUnlockMutexes = TRUE; - } - - // Try to free everything in the heap list - - pTmpRCache = gv_FlmSysData.RCacheMgr.pHeapList; - - while( pTmpRCache) - { - uiRecordsExamined++; - - // Save the pointer to the next entry in the list because - // we may end up unlinking pTmpRCache below - - pNextRCache = pTmpRCache->pNextInHeapList; - - // Determine if the item can be freed - - flmAssert( RCA_IS_IN_HEAP_LIST( pTmpRCache->uiFlags)); - - if( !RCA_IS_IN_USE( pTmpRCache->uiFlags) && - !RCA_IS_READING_IN( pTmpRCache->uiFlags)) - { - flmRcaFreeCache( pTmpRCache, FALSE); - } - - pTmpRCache = pNextRCache; - } - - // Now, free any old versions that are no longer needed - - flmRcaReduceCache( TRUE); - pTmpRCache = gv_FlmSysData.RCacheMgr.pLRURecord; - - // Stay in the loop until we have freed all old records, or - // we have run through the entire list. - - for( ;;) - { - if( !pTmpRCache || !gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes) - { - break; - } - - // After each 200 records examined, see if our maximum - // time has elapsed for examining without a pause. - - if( uiRecordsExamined >= 200) - { - uiRecordsExamined = 0; - uiCurrTime = FLM_GET_TIMER(); - - if( FLM_ELAPSED_TIME( uiCurrTime, uiLastTimePaused) >= uiMaxLockTime) - { - // IMPORTANT! Don't stop and pause on one that is - // being read in. - - while( pTmpRCache && RCA_IS_READING_IN( pTmpRCache->uiFlags)) - { - pTmpRCache = pTmpRCache->pPrevInGlobal; - } - - if( !pTmpRCache) - { - break; - } - - if( bUnlockMutexes) - { - // Increment the use count so that this item will not - // go away while we are paused. - - RCA_INCR_USE_COUNT( pTmpRCache->uiFlags); - f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); - f_mutexUnlock( gv_FlmSysData.hShareMutex); - - // Shortest possible pause - to allow other threads - // to do work. - - #ifdef FLM_NLM - f_yieldCPU(); - #else - f_sleep( 0); - #endif - - // Relock the mutexes - - uiLastTimePaused = FLM_GET_TIMER(); - f_mutexLock( gv_FlmSysData.hShareMutex); - f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); - - // Decrement use count that was added above. - - RCA_DECR_USE_COUNT( pTmpRCache->uiFlags); - } - - // If the item was purged while we were paused, - // finish the job and then start again at the - // top of the list. - - if( RCA_IS_PURGED( pTmpRCache->uiFlags)) - { - if( !RCA_IS_IN_USE( pTmpRCache->uiFlags)) - { - flmRcaFreePurged( pTmpRCache); - } - - pTmpRCache = gv_FlmSysData.RCacheMgr.pLRURecord; - continue; - } - } - } - - uiRecordsExamined++; - - // Save the pointer to the previous entry in the list because - // we may end up unlinking pTmpRCache below, in which case we would - // have lost the previous entry. - - pPrevRCache = pTmpRCache->pPrevInGlobal; - - // Block must not currently be in use, - // Must not be the most current version of a block, - // Cannot be dirty in any way, - // Cannot be in the process of being read in from disk, - // And must not be needed by a read transaction. - - if( !RCA_IS_IN_USE( pTmpRCache->uiFlags) && - !RCA_IS_READING_IN( pTmpRCache->uiFlags) && - (RCA_IS_IN_HEAP_LIST( pTmpRCache->uiFlags) || - (pTmpRCache->uiHighTransId != 0xFFFFFFFF && - !flmNeededByReadTrans( pTmpRCache->pFile, - pTmpRCache->uiLowTransId, - pTmpRCache->uiHighTransId)))) - { - flmRcaFreeCache( pTmpRCache, FALSE); - } - - pTmpRCache = pPrevRCache; - } - - // Defragment memory - - gv_FlmSysData.RCacheMgr.pRCacheAlloc->defragmentMemory(); - gv_FlmSysData.RCacheMgr.pRecAlloc->defragmentMemory(); - gv_FlmSysData.RCacheMgr.pRecBufAlloc->defragmentMemory(); - - // Unlock the mutexes - - if( bUnlockMutexes) - { - f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); - f_mutexUnlock( gv_FlmSysData.hShareMutex); - } -} - -/**************************************************************************** -Desc: This routine reduces record cache down to the limit expected. -****************************************************************************/ -void flmRcaReduceCache( - FLMBOOL bMutexAlreadyLocked) -{ - RCACHE * pRCache; - RCACHE * pPrevRCache; - - // Make sure the mutex is locked. - - if (!bMutexAlreadyLocked) - { - f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); - } - - pRCache = gv_FlmSysData.RCacheMgr.pLRURecord; - - // Free things until we get down below our memory limit. - - while( gv_FlmSysData.RCacheMgr.pRCacheAlloc->getTotalBytesAllocated() > - gv_FlmSysData.RCacheMgr.Usage.uiMaxBytes) - { - if( !pRCache) - { - break; - } - - // If the total of block and record cache is below the global - // cache maximum, there is no need to reduce record cache. - - if( (gv_FlmSysData.RCacheMgr.pRCacheAlloc->getTotalBytesAllocated() + - gv_FlmSysData.SCacheMgr.Usage.uiTotalBytesAllocated) <= - gv_FlmSysData.uiMaxCache) - { - break; - } - - pPrevRCache = pRCache->pPrevInGlobal; - if (!(RCA_IS_IN_USE( pRCache->uiFlags)) && - !(RCA_IS_READING_IN( pRCache->uiFlags))) - { - flmRcaFreeCache( pRCache, FALSE); - } - pRCache = pPrevRCache; - } - - if (!bMutexAlreadyLocked) - { - f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); - } -} - -/**************************************************************************** -Desc: This routine finds a record in the record cache. If it cannot - find the record, it will return the position where the record should - be inserted. NOTE: This routine assumes that the record cache mutex - has been locked. -****************************************************************************/ -void flmRcaFindRec( - FLMUINT uiContainer, - FLMUINT uiDrn, - FFILE_p pFile, - FLMUINT uiVersionNeeded, - FLMBOOL bDontPoisonCache, - FLMUINT * puiNumLooks, - RCACHE ** ppRCache, - RCACHE ** ppNewerRCache, - RCACHE ** ppOlderRCache) -{ - RCACHE * pRCache; - FLMUINT uiNumLooks = 0; - FLMBOOL bFound; - RCACHE * pNewerRCache; - RCACHE * pOlderRCache; - - // Search down the hash bucket for the matching item. - -Start_Find: - - // NOTE: Need to always calculate hash bucket because - // the hash table may have been changed while we - // were waiting to be notified below - mutex can - // be unlocked, but it is guaranteed to be locked - // here. - - pRCache = *(FLM_RCA_HASH( uiDrn)); - bFound = FALSE; - uiNumLooks = 1; - while ((pRCache) && - ((pRCache->uiDrn != uiDrn) || - (pRCache->uiContainer != uiContainer) || - (pRCache->pFile != pFile))) - { - if ((pRCache = pRCache->pNextInBucket) != NULL) - { - uiNumLooks++; - } - } - - // If we found the record, see if we have the right version. - - if (!pRCache) - { - pNewerRCache = pOlderRCache = NULL; - } - else - { - pNewerRCache = NULL; - pOlderRCache = pRCache; - for (;;) - { - - // If this one is being read in, we need to wait on it. - - if (RCA_IS_READING_IN( pRCache->uiFlags)) - { - // We need to wait for this record to be read in - // in case it coalesces with other versions, resulting - // in a version that satisfies our request. - - gv_FlmSysData.RCacheMgr.uiIoWaits++; - if (RC_BAD( flmRcaWaitNotify( &pRCache->pNotifyList))) - { - - // Don't care what error the other thread had reading - // the thing in from disk - we'll bail out and start - // our find again. - - goto Start_Find; - } - - // The thread doing the notify "uses" the record cache - // on behalf of this thread to prevent the record - // from being replaced after it unlocks the mutex. - // At this point, since we have locked the mutex, - // we need to release the record - because we - // will put a "use" on it below. - - RCA_DECR_USE_COUNT( pRCache->uiFlags); - - if (RCA_IS_PURGED( pRCache->uiFlags)) - { - if (!RCA_IS_IN_USE( pRCache->uiFlags)) - { - flmRcaFreePurged( pRCache); - } - } - - // Start over with the find because the list - // structure has changed. - - goto Start_Find; - } - - // See if this record version is the one we need. - - if (uiVersionNeeded < pRCache->uiLowTransId) - { - pNewerRCache = pRCache; - if ((pOlderRCache = pRCache = pRCache->pOlderVersion) == NULL) - { - break; - } - uiNumLooks++; - } - else if (uiVersionNeeded <= pRCache->uiHighTransId) - { - // Make this the MRU record. - - if (puiNumLooks) - { - if (bDontPoisonCache) - { - flmRcaStepUpInGlobalList( pRCache); - } - else if (pRCache->pPrevInGlobal) - { - flmRcaUnlinkFromGlobal( pRCache); - flmRcaLinkToGlobalAsMRU( pRCache); - } - - gv_FlmSysData.RCacheMgr.Usage.uiCacheHits++; - gv_FlmSysData.RCacheMgr.Usage.uiCacheHitLooks += uiNumLooks; - } - - bFound = TRUE; - break; - } - else - { - pOlderRCache = pRCache; - pNewerRCache = pRCache->pNewerVersion; - - // Set pRCache to NULL as an indicator that we did not - // find the version we needed. - - pRCache = NULL; - break; - } - } - } - - *ppRCache = pRCache; - *ppOlderRCache = pOlderRCache; - *ppNewerRCache = pNewerRCache; - - if (puiNumLooks) - { - *puiNumLooks = uiNumLooks; - } -} - -/**************************************************************************** -Desc: This routine replaces the FlmRecord that a record is pointing to - with a new one. NOTE: This routine assumes that the record cache - mutex is already locked. -****************************************************************************/ -FSTATIC void flmRcaSetRecord( - RCACHE * pRCache, - FlmRecord * pNewRecord) -{ - FLMUINT uiTotalMemory = 0; - FLMUINT uiFreeMemory; - FlmRecord * pOldRecord; - - // Release the cache's pointer to the old record data - - if ((pOldRecord = pRCache->pRecord) != NULL) - { - if( RCA_IS_IN_HEAP_LIST( pRCache->uiFlags)) - { - flmRcaUnlinkFromHeapList( pRCache); - } - - uiTotalMemory = pOldRecord->getTotalMemory(); - uiFreeMemory = pOldRecord->getFreeMemory(); - flmAssert( uiTotalMemory >= uiFreeMemory); - FlmRecordExt::clearCached( pOldRecord); - FlmRecordExt::Release( pOldRecord, TRUE); - pRCache->pRecord = NULL; - } - - if (pRCache->uiHighTransId != 0xFFFFFFFF) - { - FLMUINT uiTotalOldMemory = uiTotalMemory + sizeof( RCACHE); - - flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes >= - uiTotalOldMemory); - flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount); - gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes -= - uiTotalOldMemory; - gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount--; - } - - // Point to the new record data. - - flmAssert( pNewRecord->getID() == pRCache->uiDrn); - flmAssert( pNewRecord->getContainerID() == pRCache->uiContainer); - pRCache->pRecord = pNewRecord; - flmAssert( !pNewRecord->isCached()); - FlmRecordExt::setCached( pNewRecord); - FlmRecordExt::AddRef( pNewRecord, TRUE); - FlmRecordExt::setReadOnly( pNewRecord); - - if( FlmRecordExt::getFlags( pNewRecord) & RCA_HEAP_BUFFER) - { - flmRcaLinkToHeapList( pRCache); - } - - uiTotalMemory = pNewRecord->getTotalMemory(); - - if (pRCache->uiHighTransId != 0xFFFFFFFF) - { - gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes += - (uiTotalMemory + sizeof( RCACHE)); - gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount++; - } - uiFreeMemory = pNewRecord->getFreeMemory(); - flmAssert( uiTotalMemory >= uiFreeMemory); -} - -/**************************************************************************** -Desc: This routine links a new RCACHE structure into the global list and - into the correct place in its hash bucket. This routine assumes that - the record cache mutex is already locked. -****************************************************************************/ -FSTATIC void flmRcaLinkIntoRCache( - RCACHE * pNewerRCache, - RCACHE * pOlderRCache, - RCACHE * pRCache, - FLMBOOL bLinkAsMRU) -{ - if( bLinkAsMRU) - { - flmRcaLinkToGlobalAsMRU( pRCache); - } - else - { - flmRcaLinkToGlobalAsLRU( pRCache); - } - - if (pNewerRCache) - { - flmRcaLinkToVerList( pRCache, pNewerRCache, pOlderRCache); - } - else - { - RCACHE * pNull = NULL; - - if (pOlderRCache) - { - flmRcaUnlinkFromHashBucket( pOlderRCache); - } - flmRcaLinkToHashBucket( pRCache); - flmRcaLinkToVerList( pRCache, pNull, pOlderRCache); - } -} - -/**************************************************************************** -Desc: This routine links a new record to its FFILE according to whether - or not it is an update transaction or a read transaction. - It coalesces out any unnecessary versions. This routine assumes - that the record cache mutex is already locked. -****************************************************************************/ -FSTATIC void flmRcaLinkToFFILE( - RCACHE * pRCache, - FFILE_p pFile, - FDB_p pDb, - FLMUINT uiLowTransId, - FLMBOOL bMostCurrent) -{ - RCACHE * pTmpRCache; -#ifdef FLM_DBG_LOG - char szTmpBuf[ 80]; -#endif - - - pRCache->uiLowTransId = uiLowTransId; - - // Before coalescing, link to FFILE. - // The following test determines if the record is an - // uncommitted version generated by the update transaction. - // If so, we mark it as such, and link it at the head of the - // FFILE list - so we can get rid of it quickly if we abort - // the transaction. - - if (flmGetDbTransType( pDb) == FLM_UPDATE_TRANS) - { - - // If we are in an update transaction, there better not - // be any newer versions in the list and the high - // transaction ID returned better be 0xFFFFFFFF. - - flmAssert( pRCache->pNewerVersion == NULL); - flmRcaSetTransID( pRCache, 0xFFFFFFFF); - - // If the low transaction ID is the same as the transaction, - // we may have modified this record during the transaction. - // Unfortunately, there is no sure way to tell, so we are - // forced to assume it may have been modified. If the - // transaction aborts, we will get rid if this version out - // of cache. - - if (uiLowTransId == pDb->LogHdr.uiCurrTransID) - { - RCA_SET_UNCOMMITTED( pRCache->uiFlags); - flmRcaLinkToFileAtHead( pRCache, pFile); - } - else - { - RCA_UNSET_UNCOMMITTED( pRCache->uiFlags); - flmRcaLinkToFileAtEnd( pRCache, pFile); - } -#ifdef FLM_DBG_LOG - f_sprintf( szTmpBuf, "RCI:L%X,H%X", - (unsigned)pRCache->uiLowTransId, - (unsigned)pRCache->uiHighTransId); - - flmDbgLogWrite( pFile->uiFFileId, pRCache->uiContainer, pRCache->uiDrn, - pDb->LogHdr.uiCurrTransID, szTmpBuf); -#endif - } - else - { - // Adjust the high transaction ID to be the same as - // the transaction ID - we may have gotten a 0xFFFFFFF - // back, but that is possible even if the record is - // not the most current version. Besides that, it is - // possible that in the mean time one or more update - // transactions have come along and created one or - // more newer versions of the record. - - FLMUINT uiHighTransId = (FLMUINT)((bMostCurrent) - ? (FLMUINT)0xFFFFFFFF - : pDb->LogHdr.uiCurrTransID); - - flmRcaSetTransID( pRCache, uiHighTransId); - - // For a read transaction, if there is a newer version, - // it better have a higher "low transaction ID" - -#ifdef FLM_DBG_LOG - f_sprintf( szTmpBuf, "RCA:L%X,H%X", - (unsigned)pRCache->uiLowTransId, - (unsigned)pRCache->uiHighTransId); - - flmDbgLogWrite( pFile->uiFFileId, pRCache->uiContainer, pRCache->uiDrn, - pDb->LogHdr.uiCurrTransID, szTmpBuf); -#endif - -#ifdef FLM_DEBUG - if (pRCache->pNewerVersion && - !RCA_IS_READING_IN( pRCache->pNewerVersion->uiFlags)) - { - flmAssert( pRCache->uiHighTransId < - pRCache->pNewerVersion->uiLowTransId); - if( pRCache->uiHighTransId >= - pRCache->pNewerVersion->uiLowTransId) - { - flmRcaCheck( pDb, pRCache->uiContainer, pRCache->uiDrn); - } - } -#endif - RCA_UNSET_UNCOMMITTED( pRCache->uiFlags); - flmRcaLinkToFileAtEnd( pRCache, pFile); - } - - // Coalesce any versions that overlap - can only - // coalesce older versions. For an updater, there - // should not be any newer versions. For a reader, it - // is impossible to know how high up it can coalesce. - // The read operation that read the record may have - // gotten back a 0xFFFFFFFF for its high transaction - // ID - but after that point in time, it is possible - // that one or more update transactions may have come - // along and created one or more newer versions that - // it would be incorrect to coalesce with. - // In reality, a read transaction has to ignore the - // 0xFFFFFFFF in the high transaction ID anyway - // because there is no way to know if it is correct. - - // Coalesce older versions. - - for (;;) - { - if ((pTmpRCache = pRCache->pOlderVersion) == NULL) - { - break; - } - - // Stop if we encounter one that is being read in. - - if (RCA_IS_READING_IN( pTmpRCache->uiFlags)) - { - break; - } - - // If there is no overlap between these two, there is - // nothing more to coalesce. - - if (pRCache->uiLowTransId > pTmpRCache->uiHighTransId) - { - break; - } - - if (pRCache->uiHighTransId <= pTmpRCache->uiHighTransId) - { - // This assert represents the following case, - // which should not be possible to hit: - - // pOlder->uiHighTransId > pRCache->uiHighTransId. - // This cannot be, because if pOlder has a higher - // transaction ID, we would have found it up above and - // not tried to have read it in. - - flmAssert( 0); -#ifdef FLM_DEBUG - flmRcaCheck( pDb, pRCache->uiContainer, pRCache->uiDrn); -#endif - } - else if (pRCache->uiLowTransId >= pTmpRCache->uiLowTransId) - { - pRCache->uiLowTransId = pTmpRCache->uiLowTransId; - flmRcaFreeCache( pTmpRCache, - (FLMBOOL)((RCA_IS_IN_USE( pTmpRCache->uiFlags) || - RCA_IS_READING_IN( pTmpRCache->uiFlags)) - ? TRUE - : FALSE)); - } - else - { - // This assert represents the following case, - // which should not be possible to hit: - - // pRCache->uiLowTransId < pOlder->uiLowTransId. - // This cannot be, because pOlder has to have been read - // in to memory by a transaction whose transaction ID is - // less than or equal to our own. That being the case, - // it would be impossible for our transaction to have - // found a version of the record that is older than pOlder. - - flmAssert( 0); -#ifdef FLM_DEBUG - flmRcaCheck( pDb, pRCache->uiContainer, pRCache->uiDrn); -#endif - } - } -} - -/**************************************************************************** -Desc: This routine retrieves a record from the record cache. -****************************************************************************/ -RCODE flmRcaRetrieveRec( - FDB * pDb, - FLMBOOL * pbTransStarted, - FLMUINT uiContainer, // Container record is in. - FLMUINT uiDrn, // DRN of record. - FLMBOOL bOkToGetFromDisk, // If not in cache, OK to get from disk? - BTSK * pStack, // Use stack to retrieve, if NON-NULL. - LFILE * pLFile, // LFILE to use, if retrieving with stack - FlmRecord ** ppRecord) -{ - RCODE rc = FERR_OK; - FLMBOOL bRCacheMutexLocked = FALSE; - FFILE_p pFile = pDb->pFile; - RCACHE * pRCache; - RCACHE * pNewerRCache; - RCACHE * pOlderRCache; - FlmRecord * pRecord = NULL; - FLMBOOL bGotFromDisk = FALSE; - FlmRecord * pNewRecord = NULL; - FlmRecord * pOldRecord = NULL; - FLMUINT uiLowTransId; - FLMBOOL bMostCurrent; - FLMUINT uiCurrTransId; - FNOTIFY_p pNotify; - FLMUINT uiNumLooks; - FLMBOOL bInitializedFdb = FALSE; - FLMBOOL bDontPoisonCache = pDb->uiFlags & FDB_DONT_POISON_CACHE - ? TRUE - : FALSE; - - if( RC_BAD( rc = flmCheckDatabaseState( pDb))) - { - goto Exit; - } - - // Get the current transaction ID - - if( pDb->uiTransType != FLM_NO_TRANS) - { - uiCurrTransId = pDb->LogHdr.uiCurrTransID; - } - else - { - flmAssert( pbTransStarted != NULL); - - f_mutexLock( gv_FlmSysData.hShareMutex); - - // Get the last committed transaction ID. - - uiCurrTransId = (FLMUINT)FB2UD( - &pFile->ucLastCommittedLogHdr[ LOG_CURR_TRANS_ID]); - - f_mutexUnlock( gv_FlmSysData.hShareMutex); - } - - flmAssert( uiDrn != 0); - - // Lock the mutex - - f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); - bRCacheMutexLocked = TRUE; - - // Reset the FDB's inactive time - - pDb->uiInactiveTime = 0; - - // See if we should resize the hash table. - - if ((gv_FlmSysData.RCacheMgr.Usage.uiCount > - FLM_RCA_MAX_REC_CNT( gv_FlmSysData.RCacheMgr.uiNumBuckets) && - gv_FlmSysData.RCacheMgr.uiNumBuckets < MAX_RCACHE_BUCKETS) || - (gv_FlmSysData.RCacheMgr.Usage.uiCount < - FLM_RCA_MIN_REC_CNT( gv_FlmSysData.RCacheMgr.uiNumBuckets) && - gv_FlmSysData.RCacheMgr.uiNumBuckets > MIN_RCACHE_BUCKETS)) - { - if (RC_BAD( rc = flmRcaRehash())) - { - goto Exit; - } - } - -Start_Find: - - flmRcaFindRec( uiContainer, uiDrn, pFile, - uiCurrTransId, bDontPoisonCache, - &uiNumLooks, &pRCache, - &pNewerRCache, &pOlderRCache); - - if (pRCache) - { - if( ppRecord) - { - goto Found_Record; - } - goto Exit; - } - - // Did not find the record, fetch from disk, if OK to do so. - - if (!bOkToGetFromDisk || !ppRecord) - { - rc = RC_SET( FERR_NOT_FOUND); - goto Exit; - } - - // Code to handle case where we are not in a transaction. - // If we are already in a transaction, we will do the - // call to fdbInit below - AFTER allocating the RCACHE, etc. - - if( pbTransStarted && pDb->uiTransType == FLM_NO_TRANS) - { - f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); - bRCacheMutexLocked = FALSE; - - if ( RC_BAD( rc = fdbInit( pDb, FLM_READ_TRANS, - FDB_TRANS_GOING_OK, 0, pbTransStarted))) - { - fdbExit( pDb); - goto Exit; - } - bInitializedFdb = TRUE; - - f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); - bRCacheMutexLocked = TRUE; - - uiCurrTransId = pDb->LogHdr.uiCurrTransID; - goto Start_Find; - } - - // Increment the number of faults only if we retrieve the record from disk. - - gv_FlmSysData.RCacheMgr.Usage.uiCacheFaults++; - gv_FlmSysData.RCacheMgr.Usage.uiCacheFaultLooks += uiNumLooks; - - // Create a place holder for the object. - - if (RC_BAD( rc = flmRcaAllocCacheStruct( &pRCache))) - { - goto Exit; - } - pRCache->uiDrn = uiDrn; - pRCache->uiContainer = uiContainer; - - // Set the FFILE so that other threads looking for this record in - // cache will find it and wait until the read has completed. If - // the FFILE is not set, other threads will attempt their own read, - // because they won't match a NULL FFILE. The result of not setting - // the FFILE is that multiple copies of the same version of a particular - // record could end up in cache. - pRCache->pFile = pFile; - - flmRcaLinkIntoRCache( pNewerRCache, pOlderRCache, - pRCache, !bDontPoisonCache); - - RCA_SET_READING_IN( pRCache->uiFlags); - RCA_INCR_USE_COUNT( pRCache->uiFlags); - pRCache->pNotifyList = NULL; - - // Unlock mutex before reading in from disk. - - f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); - bRCacheMutexLocked = FALSE; - - if( pbTransStarted && !bInitializedFdb) - { - if ( RC_BAD( rc = fdbInit( pDb, FLM_READ_TRANS, - FDB_TRANS_GOING_OK, 0, pbTransStarted))) - { - fdbExit( pDb); - goto Notify_Waiters; - } - bInitializedFdb = TRUE; - } - - // Read record from disk. - -#ifdef FLM_DBG_LOG - flmDbgLogWrite( pFile->uiFFileId, uiContainer, - uiDrn, pDb->LogHdr.uiCurrTransID, "RRD"); -#endif - - if (pStack) - { - rc = FSReadElement( pDb, &pDb->TempPool, pLFile, - uiDrn, pStack, TRUE, ppRecord, - &uiLowTransId, &bMostCurrent); - } - else if ((pLFile) || - (RC_OK( rc = fdictGetContainer( pDb->pDict, uiContainer, &pLFile)))) - { - rc = FSReadRecord( pDb, pLFile, uiDrn, ppRecord, - &uiLowTransId, &bMostCurrent); - } - -Notify_Waiters: - - // Relock mutex - - f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); - bRCacheMutexLocked = TRUE; - - // If read was successful, link the record to its place in - // the FFILE list and coalesce any versions that overlap - // this one. - - if (RC_OK( rc)) - { - flmRcaLinkToFFILE( pRCache, - pDb->pFile, pDb, uiLowTransId, bMostCurrent); - } - - RCA_UNSET_READING_IN( pRCache->uiFlags); - - // Notify any threads waiting for the read to complete. - - pNotify = pRCache->pNotifyList; - pRCache->pNotifyList = NULL; - if (pNotify) - { - flmRcaNotify( pNotify, - (RCACHE *)((RC_BAD( rc)) - ? NULL - : pRCache), rc); - } - RCA_DECR_USE_COUNT( pRCache->uiFlags); - - // If we did not succeed, free the RCACHE structure. - - if (RC_BAD( rc)) - { - flmRcaFreeCache( pRCache, FALSE); - goto Exit; - } - - // If this item was purged while we were reading it in, - // start over with the search. - - if (RCA_IS_PURGED( pRCache->uiFlags)) - { - if (!RCA_IS_IN_USE( pRCache->uiFlags)) - { - flmRcaFreePurged( pRCache); - } - - // Start over with the find - this one has - // been marked for purging. - - pNewRecord = NULL; - pOldRecord = NULL; - bGotFromDisk = FALSE; - goto Start_Find; - } - - // When reading from disk, no need to verify that we - // are getting the app implementation - because we - // always will. - - bGotFromDisk = TRUE; - pRecord = *ppRecord; - flmRcaSetRecord( pRCache, pRecord); - -Found_Record: - - if (!bGotFromDisk) - { - if( (pRecord = *ppRecord) != pRCache->pRecord) - { - if (*ppRecord) - { - FlmRecordExt::Release( *ppRecord, bRCacheMutexLocked); - } - - pRecord = *ppRecord = pRCache->pRecord; - FlmRecordExt::AddRef( pRecord, bRCacheMutexLocked); - } - } - - // Clean up cache, if necessary. - - if (gv_FlmSysData.RCacheMgr.pRCacheAlloc->getTotalBytesAllocated() > - gv_FlmSysData.RCacheMgr.Usage.uiMaxBytes) - { - flmRcaReduceCache( bRCacheMutexLocked); - } - -Exit: - - if (pNewRecord) - { - FlmRecordExt::Release( pNewRecord, bRCacheMutexLocked); - } - - if (bRCacheMutexLocked) - { - f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); - } - - if (bInitializedFdb) - { - fdbExit(pDb); - } - - return( rc); -} - -/**************************************************************************** -Desc: This routine inserts a record into the record cache. This is ONLY - called by FlmRecordModify or FlmRecordAdd. In the case of a modify, - this should replace any uncommitted version of the record that may - have been put there by a prior call to FlmRecordModify. -****************************************************************************/ -RCODE flmRcaInsertRec( - FDB * pDb, - FLMUINT uiContainer, // Container record is in. - FLMUINT uiDrn, // DRN of record - FlmRecord * pRecord) // Record to be inserted. -{ - RCODE rc = FERR_OK; - FFILE_p pFile = pDb->pFile; - FLMBOOL bMutexLocked = FALSE; - RCACHE * pRCache; - RCACHE * pNewerRCache; - RCACHE * pOlderRCache; - FLMBOOL bDontPoisonCache = pDb->uiFlags & FDB_DONT_POISON_CACHE - ? TRUE - : FALSE; - - flmAssert( uiDrn != 0); - - // Lock the mutex - - f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); - bMutexLocked = TRUE; - - if ((gv_FlmSysData.RCacheMgr.Usage.uiCount > - FLM_RCA_MAX_REC_CNT( gv_FlmSysData.RCacheMgr.uiNumBuckets) && - gv_FlmSysData.RCacheMgr.uiNumBuckets < MAX_RCACHE_BUCKETS) || - (gv_FlmSysData.RCacheMgr.Usage.uiCount < - FLM_RCA_MIN_REC_CNT( gv_FlmSysData.RCacheMgr.uiNumBuckets) && - gv_FlmSysData.RCacheMgr.uiNumBuckets > MIN_RCACHE_BUCKETS)) - { - if (RC_BAD( rc = flmRcaRehash())) - { - goto Exit; - } - } - - // See if we can find the record in cache - - flmRcaFindRec( uiContainer, uiDrn, pFile, - pDb->LogHdr.uiCurrTransID, bDontPoisonCache, NULL, &pRCache, - &pNewerRCache, &pOlderRCache); - - if (pRCache) - { - - // If we found the last committed version, instead of replacing it, - // we want to change its high transaction ID, and go create a new - // record to put in cache. - - if (pRCache->uiLowTransId < pDb->LogHdr.uiCurrTransID) - { - - // pOlderRCache and pRCache should be the same at this point if we - // found something. Furthermore, the high transaction ID on what - // we found better be -1 - most current version. - - flmAssert( pOlderRCache == pRCache); - flmAssert( pOlderRCache->uiHighTransId == 0xFFFFFFFF); - - flmRcaSetTransID( pOlderRCache, (pDb->LogHdr.uiCurrTransID - 1)); - - flmAssert( pOlderRCache->uiHighTransId >= pOlderRCache->uiLowTransId); - - RCA_SET_UNCOMMITTED( pOlderRCache->uiFlags); - RCA_SET_LATEST_VER( pOlderRCache->uiFlags); - flmRcaUnlinkFromFile( pOlderRCache); - flmRcaLinkToFileAtHead( pOlderRCache, pFile); - } - else - { - // Found latest UNCOMMITTED VERSION - replace it. - -#ifdef FLM_CHECK_RECORD - if (RC_BAD( rc = pRecord->checkRecord())) - { - goto Exit; - } -#endif - - if (RC_BAD( rc = pRecord->compressMemory())) - { - goto Exit; - } - -#ifdef FLM_CHECK_RECORD - if (RC_BAD( rc = pRecord->checkRecord())) - { - goto Exit; - } -#endif - - // Replace the old record data with the new record data. - - flmRcaSetRecord( pRCache, pRecord); - - // Make sure we set the "uncommitted" flag and move the record - // to the head of the FFILE's list. - - if (!RCA_IS_UNCOMMITTED( pRCache->uiFlags)) - { - RCA_SET_UNCOMMITTED( pRCache->uiFlags); - flmRcaUnlinkFromFile( pRCache); - flmRcaLinkToFileAtHead( pRCache, pFile); - } - - // Will not have already been put at MRU if bDonPoisonCache is TRUE. - - if (pRCache->pPrevInGlobal) - { - flmRcaUnlinkFromGlobal( pRCache); - flmRcaLinkToGlobalAsMRU( pRCache); - } - - goto Exit; - } - } - - // We are positioned to insert the new record. For an update, it - // must always be the newest version. - - flmAssert( pNewerRCache == NULL); - -#ifdef FLM_CHECK_RECORD - if (RC_BAD( rc = pRecord->checkRecord())) - { - goto Exit; - } -#endif - - if (RC_BAD( rc = pRecord->compressMemory())) - { - goto Exit; - } - -#ifdef FLM_CHECK_RECORD - if (RC_BAD( rc = pRecord->checkRecord())) - { - goto Exit; - } -#endif - - // Allocate a new RCACHE structure. - - if (RC_BAD( rc = flmRcaAllocCacheStruct( &pRCache))) - { - goto Exit; - } - - // Set the DRN and container for the structure. - - pRCache->uiDrn = uiDrn; - pRCache->uiContainer = uiContainer; - pRCache->pFile = pFile; - - // Link into the global list and hash bucket. - - flmRcaLinkIntoRCache( pNewerRCache, pOlderRCache, pRCache, TRUE); - - // Link to its FFILE and coalesce out duplicates. - // NOTE: This routine automatically puts an uncommitted version - // at the head of the FFILE's list and sets the uncommitted flag. - - flmRcaLinkToFFILE( pRCache, pFile, pDb, pDb->LogHdr.uiCurrTransID, TRUE); - - // Set the record data pointer for the RCACHE structure. This will also - // release the old data pointer and set the read only flag and the mutex - // for the record data. Also updates memory usage fields in RCacheMgr. - - flmRcaSetRecord( pRCache, pRecord); - - // Clean up cache, if necessary. - - flmRcaReduceCache( bMutexLocked); - -Exit: - - if (bMutexLocked) - { - f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); - } - - return( rc); -} - -/**************************************************************************** -Desc: This routine is called by FlmRecordDelete to remove a record from - cache. If there is an uncommitted version of the record, it should - remove that version from cache. If the last committed version is in - cache, it should set the high transaction ID on that version to be - one less than the transaction ID of the update transaction that is - doing the FlmRecordDelete call. -****************************************************************************/ -RCODE flmRcaRemoveRec( - FDB * pDb, - FLMUINT uiContainer, - FLMUINT uiDrn) -{ - RCODE rc = FERR_OK; - FLMBOOL bMutexLocked = FALSE; - RCACHE * pRCache; - RCACHE * pNewerRCache; - RCACHE * pOlderRCache; - FFILE_p pFile = pDb->pFile; - - flmAssert( uiDrn != 0); - - // Lock the semaphore - - bMutexLocked = TRUE; - f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); - - if ((gv_FlmSysData.RCacheMgr.Usage.uiCount > - FLM_RCA_MAX_REC_CNT( gv_FlmSysData.RCacheMgr.uiNumBuckets) && - gv_FlmSysData.RCacheMgr.uiNumBuckets < MAX_RCACHE_BUCKETS) || - (gv_FlmSysData.RCacheMgr.Usage.uiCount < - FLM_RCA_MIN_REC_CNT( gv_FlmSysData.RCacheMgr.uiNumBuckets) && - gv_FlmSysData.RCacheMgr.uiNumBuckets > MIN_RCACHE_BUCKETS)) - { - if (RC_BAD( rc = flmRcaRehash())) - { - goto Exit; - } - } - - // See if we can find the record in cache - - flmRcaFindRec( uiContainer, uiDrn, pFile, - pDb->LogHdr.uiCurrTransID, FALSE, NULL, &pRCache, - &pNewerRCache, &pOlderRCache); - - if (pRCache) - { - // FlmRecordDelete is calling this routine, so we determine if we found - // the last committed version or a record that was added by this same - // transaction. If we found the last committed version, set its high - // transaction ID. Otherwise, remove the record from cache. - - if (pRCache->uiLowTransId < pDb->LogHdr.uiCurrTransID) - { - - // pOlderRCache and pRCache should be the same at this point if we - // found something. Furthermore, the high transaction ID on what - // we found better be -1 - most current version. - - flmAssert( pOlderRCache == pRCache); - flmAssert( pOlderRCache->uiHighTransId == 0xFFFFFFFF); - - flmRcaSetTransID( pOlderRCache, (pDb->LogHdr.uiCurrTransID - 1)); - flmAssert( pOlderRCache->uiHighTransId >= pOlderRCache->uiLowTransId); - RCA_SET_UNCOMMITTED( pOlderRCache->uiFlags); - RCA_SET_LATEST_VER( pOlderRCache->uiFlags); - flmRcaUnlinkFromFile( pOlderRCache); - flmRcaLinkToFileAtHead( pOlderRCache, pFile); - } - else - { - flmRcaFreeCache( pRCache, - (FLMBOOL)(RCA_IS_IN_USE( pRCache->uiFlags) - ? TRUE - : FALSE)); - } - } - - // Take the opportunity to clean up cache. - - flmRcaReduceCache( bMutexLocked); - -Exit: - - if (bMutexLocked) - { - f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); - } - - return( rc); -} - -/**************************************************************************** -Desc: This routine is called when an FFILE structure is going to be removed - from the shared memory area. At that point, we also need to get rid - of all records that have been cached for that FFILE. -****************************************************************************/ -void flmRcaFreeFileRecs( - FFILE_p pFile) -{ - FLMUINT uiNumFreed = 0; - - f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); - while (pFile->pFirstRecord) - { - flmRcaFreeCache( pFile->pFirstRecord, - RCA_IS_IN_USE( pFile->pFirstRecord->uiFlags) - ? TRUE - : FALSE); - - // Release the CPU every 100 records freed. - - if (uiNumFreed < 100) - { - uiNumFreed++; - } - else - { - f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); - f_yieldCPU(); - f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); - uiNumFreed = 0; - } - } - f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); -} - -/**************************************************************************** -Desc: This routine is called when an update transaction aborts. At that - point, we need to get rid of any uncommitted versions of records in - the record cache. -****************************************************************************/ -void flmRcaAbortTrans( - FDB * pDb - ) -{ - FFILE_p pFile = pDb->pFile; - RCACHE * pRCache; - RCACHE * pOlderVersion; - FLMUINT uiOlderTransId = - pDb->LogHdr.uiCurrTransID - 1; - - f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); - pRCache = pFile->pFirstRecord; - while (pRCache) - { - if (RCA_IS_UNCOMMITTED( pRCache->uiFlags)) - { - if (RCA_IS_LATEST_VER( pRCache->uiFlags)) - { - flmRcaSetTransID( pRCache, 0xFFFFFFFF); - RCA_UNSET_UNCOMMITTED( pRCache->uiFlags); - RCA_UNSET_LATEST_VER( pRCache->uiFlags); - flmRcaUnlinkFromFile( pRCache); - flmRcaLinkToFileAtEnd( pRCache, pFile); - } - else - { - // Save the older version - we may be changing its - // high transaction ID back to 0xFFFFFFFF - - pOlderVersion = pRCache->pOlderVersion; - - // Free the uncommitted version. - - flmRcaFreeCache( pRCache, - (FLMBOOL)((RCA_IS_IN_USE( pRCache->uiFlags) || - RCA_IS_READING_IN( pRCache->uiFlags)) - ? TRUE - : FALSE)); - - // If the older version has a high transaction ID that - // is exactly one less than our current transaction, - // it is the most current version. Hence, we need to - // change its high transaction ID back to 0xFFFFFFFF. - - if ((pOlderVersion) && - (pOlderVersion->uiHighTransId == uiOlderTransId)) - { - flmRcaSetTransID( pOlderVersion, 0xFFFFFFFF); - } - } - pRCache = pFile->pFirstRecord; - } - else - { - // We can stop when we hit a committed version, because - // uncommitted versions are always linked in together at - // the head of the list. - - break; - } - } - f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); -} - -/**************************************************************************** -Desc: This routine is called when an update transaction commits. At that - point, we need to unset the "uncommitted" flag on any records - currently in record cache for the FFILE. -****************************************************************************/ -void flmRcaCommitTrans( - FDB * pDb) -{ - RCACHE * pRCache; - - f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); - pRCache = pDb->pFile->pFirstRecord; - while (pRCache) - { - if (RCA_IS_UNCOMMITTED( pRCache->uiFlags)) - { - RCA_UNSET_UNCOMMITTED( pRCache->uiFlags); - RCA_UNSET_LATEST_VER( pRCache->uiFlags); - pRCache = pRCache->pNextInFile; - } - else - { - - // We can stop when we hit a committed version, because - // uncommitted versions are always linked in together at - // the head of the list. - - break; - } - } - f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); -} - -/**************************************************************************** -Desc: This routine is called when a container in the database is deleted. - All records in record cache that are in that container must be - removed from cache. -****************************************************************************/ -void flmRcaRemoveContainerRecs( - FDB * pDb, - FLMUINT uiContainer) -{ - FFILE * pFile = pDb->pFile; - RCACHE * pRCache; - RCACHE * pPrevRCache; - FLMUINT uiTransId = pDb->LogHdr.uiCurrTransID; - - f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); - pRCache = gv_FlmSysData.RCacheMgr.pLRURecord; - - // Stay in the loop until we have freed all old records, or - // we have run through the entire list. - - while (pRCache) - { - // Save the pointer to the previous entry in the list because - // we may end up unlinking pRCache below, in which case we would - // have lost the previous entry. - - pPrevRCache = pRCache->pPrevInGlobal; - - // Only look at records in this container and this database. - - if ((pRCache->uiContainer == uiContainer) && - (pRCache->pFile == pFile)) - { - - // Only look at the most current versions. - - if (pRCache->uiHighTransId == 0xFFFFFFFF) - { - - // Better not be a newer version. - - flmAssert( pRCache->pNewerVersion == NULL); - - if (pRCache->uiLowTransId < uiTransId) - { - - // This version was not added or modified by this - // transaction so it's high transaction ID should simply - // be set to one less than the current transaction ID. - - flmRcaSetTransID( pRCache, uiTransId - 1); - flmAssert( pRCache->uiHighTransId >= pRCache->uiLowTransId); - RCA_SET_UNCOMMITTED( pRCache->uiFlags); - RCA_SET_LATEST_VER( pRCache->uiFlags); - flmRcaUnlinkFromFile( pRCache); - flmRcaLinkToFileAtHead( pRCache, pFile); - } - else - { - - // The record was added or modified in this - // transaction. Simply remove it from cache. - - flmRcaFreeCache( pRCache, - (FLMBOOL)(RCA_IS_IN_USE( pRCache->uiFlags) - ? TRUE - : FALSE)); - } - } - else - { - - // If not most current version, the record's high transaction - // ID better already be less than transaction ID. - - flmAssert( pRCache->uiHighTransId < uiTransId); - } - } - pRCache = pPrevRCache; - - } - f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); -} - -/**************************************************************************** -Desc: -****************************************************************************/ -#ifdef FLM_DEBUG -FSTATIC RCODE flmRcaCheck( - FDB_p pDb, - FLMUINT uiContainer, - FLMUINT uiDrn) -{ - LFILE * pLFile; - FlmRecord * pRecord = NULL; - FLMUINT uiLowTransId; - FLMBOOL bMostCurrent; - RCODE rc; - - if( RC_OK( rc = fdictGetContainer( pDb->pDict, uiContainer, &pLFile))) - { - rc = FSReadRecord( pDb, pLFile, uiDrn, &pRecord, - &uiLowTransId, &bMostCurrent); - } - - if( pRecord) - { - pRecord->Release(); - } - - return( rc); -} -#endif - -/**************************************************************************** -Desc: -****************************************************************************/ -FLMBOOL FlmRecordExt::canRelocateRec( - void * pvAlloc) -{ - FlmRecord * pRec = (FlmRecord *)pvAlloc; - - if( pRec->getRefCount() == 1 && pRec->isCached()) - { - return( TRUE); - } - - return( FALSE); -} - -/**************************************************************************** -Desc: -****************************************************************************/ -void FlmRecordExt::relocateRec( - void * pvOldAlloc, - void * pvNewAlloc) -{ - FlmRecord * pOldRec = (FlmRecord *)pvOldAlloc; - FlmRecord * pNewRec = (FlmRecord *)pvNewAlloc; - RCACHE * pRCache; - RCACHE * pVersion; - - flmAssert( pOldRec->getRefCount() == 1); - flmAssert( pOldRec->isCached()); - flmAssert( pvNewAlloc < pvOldAlloc); - - // Update the record pointer in the data buffer - - if( pNewRec->m_pucBuffer) - { - flmAssert( *((FlmRecord **)pOldRec->m_pucBuffer) == pOldRec); - *((FlmRecord **)pNewRec->m_pucBuffer) = pNewRec; - } - - // Find the record - - pRCache = *(FLM_RCA_HASH( pNewRec->m_uiRecordID)); - - while( pRCache) - { - if( pRCache->uiDrn == pNewRec->m_uiRecordID) - { - pVersion = pRCache; - - while( pVersion) - { - if( pVersion->pRecord == pOldRec) - { - pVersion->pRecord = pNewRec; - goto Done; - } - - pVersion = pVersion->pOlderVersion; - } - } - - pRCache = pRCache->pNextInBucket; - } - -Done: - - flmAssert( pRCache); -} - -/**************************************************************************** -Desc: -****************************************************************************/ -FLMBOOL FlmRecordExt::canRelocateRecBuffer( - void * pvAlloc) -{ - FlmRecord * pRec = *((FlmRecord **)pvAlloc); - - flmAssert( pRec->m_pucBuffer == pvAlloc); - - if( pRec->getRefCount() == 1 && pRec->isCached()) - { - return( TRUE); - } - - return( FALSE); -} - -/**************************************************************************** -Desc: -****************************************************************************/ -void FlmRecordExt::relocateRecBuffer( - void * pvOldAlloc, - void * pvNewAlloc) -{ - FlmRecord * pRec = *((FlmRecord **)pvOldAlloc); - - flmAssert( pRec->getRefCount() == 1); - flmAssert( pRec->isCached()); - flmAssert( pRec->m_pucBuffer == pvOldAlloc); - flmAssert( pvNewAlloc < pvOldAlloc); - - // Update the buffer pointer in the record - - pRec->m_pucBuffer = (FLMBYTE *)pvNewAlloc; -} - -/**************************************************************************** -Desc: -Notes: This routine assumes the rcache mutex is locked -****************************************************************************/ -FSTATIC FLMBOOL rcaCanRelocate( - void * pvAlloc) -{ - RCACHE * pRCache = (RCACHE *)pvAlloc; - - if( RCA_IS_IN_USE( pRCache->uiFlags) || - RCA_IS_READING_IN( pRCache->uiFlags)) - { - return( FALSE); - } - - return( TRUE); -} - -/**************************************************************************** -Desc: Fixes up all pointers needed to allow an RCACHE struct to be - moved to a different location in memory -Notes: This routine assumes the rcache mutex is locked -****************************************************************************/ -FSTATIC void rcaRelocate( - void * pvOldAlloc, - void * pvNewAlloc) -{ - RCACHE * pOldRCache = (RCACHE *)pvOldAlloc; - RCACHE * pNewRCache = (RCACHE *)pvNewAlloc; - RCACHE ** ppBucket; - RCACHE_MGR * pRCacheMgr = &gv_FlmSysData.RCacheMgr; - FFILE * pFile = pOldRCache->pFile; - - flmAssert( !RCA_IS_IN_USE( pOldRCache->uiFlags)); - flmAssert( !RCA_IS_READING_IN( pOldRCache->uiFlags)); - flmAssert( !pOldRCache->pNotifyList); - - if( pNewRCache->pPrevInFile) - { - pNewRCache->pPrevInFile->pNextInFile = pNewRCache; - } - - if( pNewRCache->pNextInFile) - { - pNewRCache->pNextInFile->pPrevInFile = pNewRCache; - } - - if( pNewRCache->pPrevInGlobal) - { - pNewRCache->pPrevInGlobal->pNextInGlobal = pNewRCache; - } - - if( pNewRCache->pNextInGlobal) - { - pNewRCache->pNextInGlobal->pPrevInGlobal = pNewRCache; - } - - if( pNewRCache->pPrevInBucket) - { - pNewRCache->pPrevInBucket->pNextInBucket = pNewRCache; - } - - if( pNewRCache->pNextInBucket) - { - pNewRCache->pNextInBucket->pPrevInBucket = pNewRCache; - } - - if( pNewRCache->pOlderVersion) - { - pNewRCache->pOlderVersion->pNewerVersion = pNewRCache; - } - - if( pNewRCache->pNewerVersion) - { - pNewRCache->pNewerVersion->pOlderVersion = pNewRCache; - } - - if( pNewRCache->pPrevInHeapList) - { - pNewRCache->pPrevInHeapList->pNextInHeapList = pNewRCache; - } - - if( pNewRCache->pNextInHeapList) - { - pNewRCache->pNextInHeapList->pPrevInHeapList = pNewRCache; - } - - ppBucket = FLM_RCA_HASH( pOldRCache->uiDrn); - if( *ppBucket == pOldRCache) - { - *ppBucket = pNewRCache; - } - - if( pRCacheMgr->pHeapList == pOldRCache) - { - pRCacheMgr->pHeapList = pNewRCache; - } - - if( pRCacheMgr->pPurgeList == pOldRCache) - { - pRCacheMgr->pPurgeList = pNewRCache; - } - - if( pRCacheMgr->pMRURecord == pOldRCache) - { - pRCacheMgr->pMRURecord = pNewRCache; - } - - if( pRCacheMgr->pLRURecord == pOldRCache) - { - pRCacheMgr->pLRURecord = pNewRCache; - } - - if( pFile) - { - if( pFile->pFirstRecord == pOldRCache) - { - pFile->pFirstRecord = pNewRCache; - } - - if( pFile->pLastRecord == pOldRCache) - { - pFile->pLastRecord = pNewRCache; - } - } - -#ifdef FLM_DEBUG - f_memset( pOldRCache, 0, sizeof( RCACHE)); -#endif -} +//------------------------------------------------------------------------- +// Desc: Record caching +// Tabs: 3 +// +// Copyright (c) 1999-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: rcache.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +#if defined( FLM_NLM) && !defined( __MWERKS__) +// Disable "Warning! W549: col(XX) 'sizeof' operand contains +// compiler generated information" + #pragma warning 549 9 +#endif + +FSTATIC FLMBOOL rcaCanRelocate( + void * pvAlloc); + +FSTATIC void rcaRelocate( + void * pvOldAlloc, + void * pvNewAlloc); + +// Extended record object for accessing private members of FlmRecord + +struct FlmRecordExt +{ + static FINLINE void clearCached( + FlmRecord * pRec) + { + pRec->clearCached(); + } + + static FINLINE void setCached( + FlmRecord * pRec) + { + pRec->setCached(); + } + + static FINLINE void setReadOnly( + FlmRecord * pRec) + { + pRec->setReadOnly(); + } + + static FINLINE FLMINT Release( + FlmRecord * pRec, + FLMBOOL bMutexLocked) + { + return( pRec->Release( bMutexLocked)); + } + + static FINLINE void setOldVersion( + FlmRecord * pRec) + { + pRec->setOldVersion(); + } + + static FINLINE void clearOldVersion( + FlmRecord * pRec) + { + pRec->clearOldVersion(); + } + + static FINLINE FLMUINT getFlags( + FlmRecord * pRec) + { + return( pRec->m_uiFlags); + } + + static FLMBOOL canRelocateRec( + void * pvAlloc); + + static void relocateRec( + void * pvOldAlloc, + void * pvNewAlloc); + + static FLMBOOL canRelocateRecBuffer( + void * pvAlloc); + + static void relocateRecBuffer( + void * pvOldAlloc, + void * pvNewAlloc); +}; + +// Functions for calculating minimum and maximum record counts for a +// given hash table size. + +#define FLM_RCA_MIN_REC_CNT(uiHashTblSz) ((uiHashTblSz) / 4) +#define FLM_RCA_MAX_REC_CNT(uiHashTblSz) ((uiHashTblSz) * 4) + +// Hash function for hashing to records in record cache. + +#define FLM_RCA_HASH( uiDrn) \ + (RCACHE **)(&(gv_FlmSysData.RCacheMgr.ppHashBuckets[(uiDrn) & \ + (gv_FlmSysData.RCacheMgr.uiHashMask)])) + +/* LOCAL STATIC FUNCTION PROTOTYPES */ + +FSTATIC void flmRcaFreePurged( + RCACHE * pRCache); + +FSTATIC void flmRcaFreeCache( + RCACHE * pRCache, + FLMBOOL bPutInPurgeList); + +FSTATIC FLMUINT flmRcaGetBestHashTblSize( + FLMUINT uiCurrRecCount); + +FSTATIC RCODE flmRcaRehash( void); + +FSTATIC RCODE flmRcaSetMemLimit( + FLMUINT uiMaxCacheBytes); + +FSTATIC RCODE flmRcaWaitNotify( + FNOTIFY ** ppNotifyListRV); + +FSTATIC void flmRcaNotify( + FNOTIFY * pNotify, + RCACHE * pUseRCache, + RCODE NotifyRc); + +FSTATIC RCODE flmRcaAllocCacheStruct( + RCACHE ** ppRCache); + +FSTATIC void flmRcaFreeCacheStruct( + RCACHE ** ppRCache); + +FSTATIC void flmRcaSetRecord( + RCACHE * pRCache, + FlmRecord * pNewRecord); + +FSTATIC void flmRcaLinkIntoRCache( + RCACHE * pNewerRCache, + RCACHE * pOlderRCache, + RCACHE * pRCache, + FLMBOOL bLinkAsMRU); + +FSTATIC void flmRcaLinkToFFILE( + RCACHE * pRCache, + FFILE_p pFile, + FDB_p pDb, + FLMUINT uiLowTransId, + FLMBOOL bMostCurrent); + +#ifdef FLM_DEBUG +FSTATIC RCODE flmRcaCheck( + FDB_p pDb, + FLMUINT uiContainer, + FLMUINT uiDrn); +#endif + +/**************************************************************************** +Desc: This inline assumes that the global mutex is locked, because + it potentially updates the cache usage statistics. +****************************************************************************/ +FINLINE void flmRcaSetTransID( + RCACHE * pRCache, + FLMUINT uiNewTransID) +{ + if (pRCache->uiHighTransId == 0xFFFFFFFF && + uiNewTransID != 0xFFFFFFFF) + { + FLMUINT uiSize = (FLMUINT)((pRCache->pRecord) + ? pRCache->pRecord->getTotalMemory() + : (FLMUINT)0) + sizeof( RCACHE); + gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes += uiSize; + gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount++; + + if( pRCache->pRecord) + { + FlmRecordExt::setOldVersion( pRCache->pRecord); + } + } + else if (pRCache->uiHighTransId != 0xFFFFFFFF && + uiNewTransID == 0xFFFFFFFF) + { + FLMUINT uiSize = (FLMUINT)((pRCache->pRecord) + ? pRCache->pRecord->getTotalMemory() + : (FLMUINT)0) + sizeof( RCACHE); + flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes >= uiSize); + flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount); + gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes -= uiSize; + gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount--; + if( pRCache->pRecord) + { + FlmRecordExt::clearOldVersion( pRCache->pRecord); + } + } + pRCache->uiHighTransId = uiNewTransID; +} + +/**************************************************************************** +Desc: This routine links a record into the global list as the MRU record. + This routine assumes that the record cache mutex has already + been locked. +****************************************************************************/ +FINLINE void flmRcaLinkToGlobalAsMRU( + RCACHE * pRCache) +{ + pRCache->pPrevInGlobal = NULL; + if ((pRCache->pNextInGlobal = gv_FlmSysData.RCacheMgr.pMRURecord) != NULL) + { + gv_FlmSysData.RCacheMgr.pMRURecord->pPrevInGlobal = pRCache; + } + else + { + gv_FlmSysData.RCacheMgr.pLRURecord = pRCache; + } + gv_FlmSysData.RCacheMgr.pMRURecord = pRCache; +} + +/**************************************************************************** +Desc: This routine links a record into the global list as the LRU record. + This routine assumes that the record cache mutex has already + been locked. +****************************************************************************/ +FINLINE void flmRcaLinkToGlobalAsLRU( + RCACHE * pRCache) +{ + pRCache->pNextInGlobal = NULL; + if ((pRCache->pPrevInGlobal = gv_FlmSysData.RCacheMgr.pLRURecord) != NULL) + { + gv_FlmSysData.RCacheMgr.pLRURecord->pNextInGlobal = pRCache; + } + else + { + gv_FlmSysData.RCacheMgr.pMRURecord = pRCache; + } + gv_FlmSysData.RCacheMgr.pLRURecord = pRCache; +} + +/**************************************************************************** +Desc: Moves a record one step closer to the MRU slot in the global list. + This routine assumes that the record cache mutex has already + been locked. +****************************************************************************/ +FINLINE void flmRcaStepUpInGlobalList( + RCACHE * pRCache) +{ + RCACHE * pPrevRCache; + + if( (pPrevRCache = pRCache->pPrevInGlobal) != NULL) + { + if( pPrevRCache->pPrevInGlobal) + { + pPrevRCache->pPrevInGlobal->pNextInGlobal = pRCache; + } + else + { + gv_FlmSysData.RCacheMgr.pMRURecord = pRCache; + } + + pRCache->pPrevInGlobal = pPrevRCache->pPrevInGlobal; + pPrevRCache->pPrevInGlobal = pRCache; + pPrevRCache->pNextInGlobal = pRCache->pNextInGlobal; + + if( pRCache->pNextInGlobal) + { + pRCache->pNextInGlobal->pPrevInGlobal = pPrevRCache; + } + else + { + gv_FlmSysData.RCacheMgr.pLRURecord = pPrevRCache; + } + pRCache->pNextInGlobal = pPrevRCache; + } +} + +/**************************************************************************** +Desc: This routine unlinks a record from the global list This routine + assumes that the record cache mutex has already been locked. +****************************************************************************/ +FINLINE void flmRcaUnlinkFromGlobal( + RCACHE * pRCache) +{ + if (pRCache->pNextInGlobal) + { + pRCache->pNextInGlobal->pPrevInGlobal = pRCache->pPrevInGlobal; + } + else + { + gv_FlmSysData.RCacheMgr.pLRURecord = pRCache->pPrevInGlobal; + } + if (pRCache->pPrevInGlobal) + { + pRCache->pPrevInGlobal->pNextInGlobal = pRCache->pNextInGlobal; + } + else + { + gv_FlmSysData.RCacheMgr.pMRURecord = pRCache->pNextInGlobal; + } + pRCache->pPrevInGlobal = pRCache->pNextInGlobal = NULL; +} + +/**************************************************************************** +Desc: This routine unlinks a record from the global purged list This routine + assumes that the record cache mutex has already been locked. +****************************************************************************/ +FINLINE void flmRcaUnlinkFromPurged( + RCACHE * pRCache) +{ + if (pRCache->pNextInGlobal) + { + pRCache->pNextInGlobal->pPrevInGlobal = pRCache->pPrevInGlobal; + } + + if (pRCache->pPrevInGlobal) + { + pRCache->pPrevInGlobal->pNextInGlobal = pRCache->pNextInGlobal; + } + else + { + gv_FlmSysData.RCacheMgr.pPurgeList = pRCache->pNextInGlobal; + } + + pRCache->pPrevInGlobal = NULL; + pRCache->pNextInGlobal = NULL; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FINLINE void flmRcaLinkToHeapList( + RCACHE * pRCache) +{ + flmAssert( !pRCache->pPrevInHeapList); + flmAssert( !pRCache->pNextInHeapList); + flmAssert( !RCA_IS_IN_HEAP_LIST( pRCache->uiFlags)); + flmAssert( FlmRecordExt::getFlags( pRCache->pRecord) & RCA_HEAP_BUFFER); + + if( (pRCache->pNextInHeapList = gv_FlmSysData.RCacheMgr.pHeapList) != NULL) + { + pRCache->pNextInHeapList->pPrevInHeapList = pRCache; + } + gv_FlmSysData.RCacheMgr.pHeapList = pRCache; + RCA_SET_IN_HEAP_LIST( pRCache->uiFlags); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FINLINE void flmRcaUnlinkFromHeapList( + RCACHE * pRCache) +{ + flmAssert( RCA_IS_IN_HEAP_LIST( pRCache->uiFlags)); + + if( pRCache->pNextInHeapList) + { + pRCache->pNextInHeapList->pPrevInHeapList = pRCache->pPrevInHeapList; + } + + if( pRCache->pPrevInHeapList) + { + pRCache->pPrevInHeapList->pNextInHeapList = pRCache->pNextInHeapList; + } + else + { + gv_FlmSysData.RCacheMgr.pHeapList = pRCache->pNextInHeapList; + } + + pRCache->pPrevInHeapList = NULL; + pRCache->pNextInHeapList = NULL; + + RCA_UNSET_IN_HEAP_LIST( pRCache->uiFlags); +} + +/**************************************************************************** +Desc: This routine links a record to an FFILE list at the head of the list. + This routine assumes that the record cache mutex has already been + locked. +****************************************************************************/ +FINLINE void flmRcaLinkToFileAtHead( + RCACHE * pRCache, + FFILE_p pFile) +{ + pRCache->pPrevInFile = NULL; + if ((pRCache->pNextInFile = pFile->pFirstRecord) != NULL) + { + pFile->pFirstRecord->pPrevInFile = pRCache; + } + else + { + pFile->pLastRecord = pRCache; + } + + pFile->pFirstRecord = pRCache; + pRCache->pFile = pFile; + RCA_SET_LINKED_TO_FILE( pRCache->uiFlags); +} + +/**************************************************************************** +Desc: This routine links a record to an FFILE list at the end of the list. + This routine assumes that the record cache mutex has already been + locked. +****************************************************************************/ +FINLINE void flmRcaLinkToFileAtEnd( + RCACHE * pRCache, + FFILE_p pFile) +{ + pRCache->pNextInFile = NULL; + if( (pRCache->pPrevInFile = pFile->pLastRecord) != NULL) + { + pFile->pLastRecord->pNextInFile = pRCache; + } + else + { + pFile->pFirstRecord = pRCache; + } + pFile->pLastRecord = pRCache; + pRCache->pFile = pFile; + RCA_SET_LINKED_TO_FILE( pRCache->uiFlags); +} + +/**************************************************************************** +Desc: This routine unlinks a record from its FFILE list. This routine + assumes that the record cache mutex has already been locked. +****************************************************************************/ +FINLINE void flmRcaUnlinkFromFile( + RCACHE * pRCache) +{ + if( RCA_IS_LINKED_TO_FILE( pRCache->uiFlags)) + { + if( pRCache->pNextInFile) + { + pRCache->pNextInFile->pPrevInFile = pRCache->pPrevInFile; + } + else + { + pRCache->pFile->pLastRecord = pRCache->pPrevInFile; + } + if( pRCache->pPrevInFile) + { + pRCache->pPrevInFile->pNextInFile = pRCache->pNextInFile; + } + else + { + pRCache->pFile->pFirstRecord = pRCache->pNextInFile; + } + pRCache->pPrevInFile = pRCache->pNextInFile = NULL; + RCA_UNSET_LINKED_TO_FILE( pRCache->uiFlags); + } +} + +/**************************************************************************** +Desc: This routine links a record into its hash bucket. This routine + assumes that the record cache mutex has already been locked. +****************************************************************************/ +FINLINE void flmRcaLinkToHashBucket( + RCACHE * pRCache) +{ + RCACHE ** ppHashBucket = FLM_RCA_HASH( pRCache->uiDrn); + + flmAssert( pRCache->pNewerVersion == NULL); + + pRCache->pPrevInBucket = NULL; + if( (pRCache->pNextInBucket = *ppHashBucket) != NULL) + { + pRCache->pNextInBucket->pPrevInBucket = pRCache; + } + *ppHashBucket = pRCache; +} + +/**************************************************************************** +Desc: This routine unlinks a record from its hash bucket. This routine + assumes that the record cache mutex has already been locked. +****************************************************************************/ +FINLINE void flmRcaUnlinkFromHashBucket( + RCACHE * pRCache) +{ + flmAssert( pRCache->pNewerVersion == NULL); + if (pRCache->pNextInBucket) + { + pRCache->pNextInBucket->pPrevInBucket = pRCache->pPrevInBucket; + } + if (pRCache->pPrevInBucket) + { + pRCache->pPrevInBucket->pNextInBucket = pRCache->pNextInBucket; + } + else + { + RCACHE ** ppHashBucket = FLM_RCA_HASH( pRCache->uiDrn); + *ppHashBucket = pRCache->pNextInBucket; + } + pRCache->pPrevInBucket = pRCache->pNextInBucket = NULL; +} + +/**************************************************************************** +Desc: This routine unlinks a record from its version list. This routine + assumes that the record cache mutex has already been locked. +****************************************************************************/ +FINLINE void flmRcaLinkToVerList( + RCACHE * pRCache, + RCACHE * pNewerVer, + RCACHE * pOlderVer) +{ + if( (pRCache->pNewerVersion = pNewerVer) != NULL) + { + pNewerVer->pOlderVersion = pRCache; + } + if ((pRCache->pOlderVersion = pOlderVer) != NULL) + { + pOlderVer->pNewerVersion = pRCache; + } +} + +/**************************************************************************** +Desc: This routine unlinks a record from its version list. This routine + assumes that the record cache mutex has already been locked. +****************************************************************************/ +FINLINE void flmRcaUnlinkFromVerList( + RCACHE * pRCache) +{ + if (pRCache->pNewerVersion) + { + pRCache->pNewerVersion->pOlderVersion = pRCache->pOlderVersion; + } + if (pRCache->pOlderVersion) + { + pRCache->pOlderVersion->pNewerVersion = pRCache->pNewerVersion; + } + pRCache->pNewerVersion = pRCache->pOlderVersion = NULL; +} + +/**************************************************************************** +Desc: This routine frees a purged from record cache. This routine assumes + that the record cache mutex has already been locked. +****************************************************************************/ +FSTATIC void flmRcaFreePurged( + RCACHE * pRCache) +{ + FLMUINT uiTotalMemory; + FLMUINT uiFreeMemory; + + // Release the record data object we are pointing to. + + if (pRCache->pRecord) + { + if( RCA_IS_IN_HEAP_LIST( pRCache->uiFlags)) + { + flmRcaUnlinkFromHeapList( pRCache); + } + + uiTotalMemory = pRCache->pRecord->getTotalMemory(); + uiFreeMemory = pRCache->pRecord->getFreeMemory(); + flmAssert( uiTotalMemory >= uiFreeMemory); + FlmRecordExt::clearCached( pRCache->pRecord); + FlmRecordExt::Release( pRCache->pRecord, TRUE); + pRCache->pRecord = NULL; + } + else + { + uiTotalMemory = 0; + uiFreeMemory = 0; + } + + // If this is an old version, decrement the old version counters. + + if (pRCache->uiHighTransId != 0xFFFFFFFF) + { + FLMUINT uiTotalOldMemory = uiTotalMemory + sizeof( RCACHE); + + flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes >= + uiTotalOldMemory); + flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount); + gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes -= uiTotalOldMemory; + gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount--; + } + + // Unlink the RCACHE from the purged list. + + flmRcaUnlinkFromPurged( pRCache); + + // Free the RCACHE structure. + + RCA_UNSET_PURGED( pRCache->uiFlags); + flmRcaFreeCacheStruct( &pRCache); +} + +/**************************************************************************** +Desc: This routine frees a record in the record cache. This routine assumes + that the record cache mutex has already been locked. +****************************************************************************/ +FSTATIC void flmRcaFreeCache( + RCACHE * pRCache, + FLMBOOL bPutInPurgeList) +{ + FLMUINT uiTotalMemory; + FLMUINT uiFreeMemory; + FLMBOOL bOldVersion; +#ifdef FLM_DBG_LOG + char szTmpBuf[ 80]; +#endif + + // Release the record data object we are pointing to. + + if (pRCache->pRecord && !bPutInPurgeList) + { + if( RCA_IS_IN_HEAP_LIST( pRCache->uiFlags)) + { + flmRcaUnlinkFromHeapList( pRCache); + } + + uiTotalMemory = pRCache->pRecord->getTotalMemory(); + uiFreeMemory = pRCache->pRecord->getFreeMemory(); + flmAssert( uiTotalMemory >= uiFreeMemory); + FlmRecordExt::clearCached( pRCache->pRecord); + FlmRecordExt::Release( pRCache->pRecord, TRUE); + pRCache->pRecord = NULL; + } + else + { + uiTotalMemory = 0; + uiFreeMemory = 0; + } + bOldVersion = (FLMBOOL)((pRCache->uiHighTransId != 0xFFFFFFFF) + ? TRUE + : FALSE); + +#ifdef FLM_DBG_LOG + f_sprintf( szTmpBuf, "RCD:H%X", + (unsigned)pRCache->uiHighTransId); + + flmDbgLogWrite( pRCache->pFile ? pRCache->pFile->uiFFileId : 0, pRCache->uiContainer, + pRCache->uiDrn, pRCache->uiLowTransId, szTmpBuf); +#endif + + // Unlink the RCACHE from its various lists. + + flmRcaUnlinkFromGlobal( pRCache); + flmRcaUnlinkFromFile( pRCache); + if (!pRCache->pNewerVersion) + { + RCACHE * pOlderVersion = pRCache->pOlderVersion; + + flmRcaUnlinkFromHashBucket( pRCache); + + // If there was an older version, it now needs to be + // put into the hash bucket. + + if (pOlderVersion) + { + flmRcaUnlinkFromVerList( pRCache); + flmRcaLinkToHashBucket( pOlderVersion); + } + } + else + { + flmRcaUnlinkFromVerList( pRCache); + } + + // Free the RCACHE structure if not putting in purge list. + + if (!bPutInPurgeList) + { + // If this was an older version, decrement the old version counters. + + if (bOldVersion) + { + FLMUINT uiTotalOldMemory = uiTotalMemory + sizeof( RCACHE); + + flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes >= + uiTotalOldMemory); + flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount); + gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount--; + gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes -= + uiTotalOldMemory; + } + flmRcaFreeCacheStruct( &pRCache); + } + else + { + if ((pRCache->pNextInGlobal = gv_FlmSysData.RCacheMgr.pPurgeList) != NULL) + { + pRCache->pNextInGlobal->pPrevInGlobal = pRCache; + } + gv_FlmSysData.RCacheMgr.pPurgeList = pRCache; + RCA_SET_PURGED( pRCache->uiFlags); + } +} + +/**************************************************************************** +Desc: This routine initializes record cache manager. +****************************************************************************/ +RCODE flmRcaInit( + FLMUINT uiMaxRecordCacheBytes) +{ + RCODE rc = FERR_OK; + + f_memset( &gv_FlmSysData.RCacheMgr, 0, sizeof( RCACHE_MGR)); + gv_FlmSysData.RCacheMgr.Usage.uiMaxBytes = uiMaxRecordCacheBytes; + gv_FlmSysData.RCacheMgr.hMutex = F_MUTEX_NULL; + + // Allocate the hash buckets. + + if (RC_BAD( rc = f_calloc( + (FLMUINT)sizeof( RCACHE *) * + (FLMUINT)MIN_RCACHE_BUCKETS, + &gv_FlmSysData.RCacheMgr.ppHashBuckets))) + { + goto Exit; + } + gv_FlmSysData.RCacheMgr.uiNumBuckets = MIN_RCACHE_BUCKETS; + gv_FlmSysData.RCacheMgr.uiHashMask = + gv_FlmSysData.RCacheMgr.uiNumBuckets - 1; + gv_FlmSysData.RCacheMgr.Usage.uiTotalBytesAllocated += + (sizeof( RCACHE *) * gv_FlmSysData.RCacheMgr.uiNumBuckets); + + // Allocate the mutex for controlling access to the + // record cache. + + if (RC_BAD( rc = f_mutexCreate( &gv_FlmSysData.RCacheMgr.hMutex))) + { + goto Exit; + } + + // Set up the RCACHE struct allocator + + if( (gv_FlmSysData.RCacheMgr.pRCacheAlloc = f_new F_FixedAlloc) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = gv_FlmSysData.RCacheMgr.pRCacheAlloc->setup( + gv_FlmSysData.pSlabManager, TRUE, sizeof( RCACHE), + &gv_FlmSysData.RCacheMgr.Usage.uiTotalBytesAllocated))) + { + goto Exit; + } + + gv_FlmSysData.RCacheMgr.pRCacheAlloc->setRelocationFuncs( + rcaCanRelocate, rcaRelocate); + + // Set up the record object allocator + + if( (gv_FlmSysData.RCacheMgr.pRecAlloc = f_new F_FixedAlloc) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = gv_FlmSysData.RCacheMgr.pRecAlloc->setup( + gv_FlmSysData.pSlabManager, + gv_FlmSysData.RCacheMgr.pRCacheAlloc->getMutex(), + sizeof( FlmRecord), + &gv_FlmSysData.RCacheMgr.Usage.uiTotalBytesAllocated))) + { + goto Exit; + } + + gv_FlmSysData.RCacheMgr.pRecAlloc->setRelocationFuncs( + FlmRecordExt::canRelocateRec, FlmRecordExt::relocateRec); + + // Set up the record buffer allocator + + if( (gv_FlmSysData.RCacheMgr.pRecBufAlloc = f_new F_BufferAlloc) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = gv_FlmSysData.RCacheMgr.pRecBufAlloc->setup( + gv_FlmSysData.pSlabManager, + gv_FlmSysData.RCacheMgr.pRCacheAlloc->getMutex(), + &gv_FlmSysData.RCacheMgr.Usage.uiTotalBytesAllocated))) + { + goto Exit; + } + + gv_FlmSysData.RCacheMgr.pRecBufAlloc->setRelocationFuncs( + FlmRecordExt::canRelocateRecBuffer, + FlmRecordExt::relocateRecBuffer); + +#ifdef FLM_DEBUG + gv_FlmSysData.RCacheMgr.bDebug = TRUE; +#endif + +Exit: + if (RC_BAD( rc)) + { + flmRcaExit(); + } + + return( rc); +} + +/**************************************************************************** +Desc: This routine determines what hash table size best fits the current + record count. It finds the hash bucket size whose midpoint between + the minimum and maximum range is closest to the record count. +****************************************************************************/ +FSTATIC FLMUINT flmRcaGetBestHashTblSize( + FLMUINT uiCurrRecCount) +{ + FLMUINT uiHashTblSize; + FLMUINT uiMaxRecsForHashTblSize; + FLMUINT uiMinRecsForHashTblSize; + FLMUINT uiClosestHashTblSize = 0; + FLMUINT uiDistanceFromMidpoint; + FLMUINT uiLowestDistanceFromMidpoint; + FLMUINT uiHashTblRecsMidpoint; + + uiLowestDistanceFromMidpoint = 0xFFFFFFFF; + for (uiHashTblSize = MIN_RCACHE_BUCKETS; + uiHashTblSize <= MAX_RCACHE_BUCKETS; + uiHashTblSize *= 2) + { + + // Maximum desirable record count for a specific hash table size + // we have arbitrarily chosen to be four times the number of buckets. + // Minimum desirable record count we have arbitrarily chosen to be + // the hash table size divided by four. + + uiMaxRecsForHashTblSize = FLM_RCA_MAX_REC_CNT( uiHashTblSize); + uiMinRecsForHashTblSize = FLM_RCA_MIN_REC_CNT( uiHashTblSize); + + // Ignore any hash bucket sizes where the current record count + // is not between the desired minimum and maximum. + + if (uiCurrRecCount >= uiMinRecsForHashTblSize && + uiCurrRecCount <= uiMaxRecsForHashTblSize) + { + + // Calculate the midpoint between the minimum and maximum + // for this particular hash table size. + + uiHashTblRecsMidpoint = (uiMaxRecsForHashTblSize - + uiMinRecsForHashTblSize) / 2; + + // See how far our current record count is from this midpoint. + + uiDistanceFromMidpoint = (FLMUINT)((uiHashTblRecsMidpoint > uiCurrRecCount) + ? (uiHashTblRecsMidpoint - uiCurrRecCount) + : (uiCurrRecCount - uiHashTblRecsMidpoint)); + + // If the distance from the midpoint is closer than our previous + // lowest distance, save it. + + if (uiDistanceFromMidpoint < uiLowestDistanceFromMidpoint) + { + uiClosestHashTblSize = uiHashTblSize; + uiLowestDistanceFromMidpoint = uiDistanceFromMidpoint; + } + } + } + + // Take the number of buckets whose middle was closest to the + // current record count; + + if (uiLowestDistanceFromMidpoint == 0xFFFFFFFF) + { + // If we did not fall between any of the minimums or maximums, + // we are either below the lowest minimum, or higher than the + // highest maximum. + + uiHashTblSize = (FLMUINT)((uiCurrRecCount < FLM_RCA_MIN_REC_CNT( MIN_RCACHE_BUCKETS)) + ? (FLMUINT)MIN_RCACHE_BUCKETS + : (FLMUINT)MAX_RCACHE_BUCKETS); + + } + else + { + uiHashTblSize = uiClosestHashTblSize; + } + return( uiHashTblSize); +} + +/**************************************************************************** +Desc: This routine resizes the hash table for the record cache manager. + NOTE: This routine assumes that the record cache mutex has been locked. +****************************************************************************/ +FSTATIC RCODE flmRcaRehash( void) +{ + RCODE rc = FERR_OK; + FLMUINT uiNewHashTblSize; + RCACHE ** ppOldHashTbl; + FLMUINT uiOldHashTblSize; + RCACHE ** ppBucket; + FLMUINT uiLoop; + RCACHE * pTmpRCache; + RCACHE * pTmpNextRCache; + + uiNewHashTblSize = flmRcaGetBestHashTblSize( + gv_FlmSysData.RCacheMgr.Usage.uiCount); + + // At this point we better have a different hash table size + // or something is mucked up! + + flmAssert( uiNewHashTblSize != + gv_FlmSysData.RCacheMgr.uiNumBuckets); + + // Save the old hash table and its size. + + ppOldHashTbl = gv_FlmSysData.RCacheMgr.ppHashBuckets; + uiOldHashTblSize = gv_FlmSysData.RCacheMgr.uiNumBuckets; + + // Allocate a new hash table. + + if (RC_BAD( rc = f_calloc( + (FLMUINT)sizeof( RCACHE *) * + (FLMUINT)uiNewHashTblSize, + &gv_FlmSysData.RCacheMgr.ppHashBuckets))) + { + gv_FlmSysData.RCacheMgr.ppHashBuckets = ppOldHashTbl; + goto Exit; + } + + // Subtract off old size and add in new size. + + gv_FlmSysData.RCacheMgr.pRCacheAlloc->decrementTotalBytesAllocated( + (sizeof( RCACHE *) * uiOldHashTblSize)); + gv_FlmSysData.RCacheMgr.pRCacheAlloc->incrementTotalBytesAllocated( + (sizeof( RCACHE *) * uiNewHashTblSize)); + + gv_FlmSysData.RCacheMgr.uiNumBuckets = uiNewHashTblSize; + gv_FlmSysData.RCacheMgr.uiHashMask = uiNewHashTblSize - 1; + + // Relink all of the records into the new + // hash table. + + for (uiLoop = 0, ppBucket = ppOldHashTbl; + uiLoop < uiOldHashTblSize; + uiLoop++, ppBucket++) + { + pTmpRCache = *ppBucket; + while (pTmpRCache) + { + pTmpNextRCache = pTmpRCache->pNextInBucket; + flmRcaLinkToHashBucket( pTmpRCache); + pTmpRCache = pTmpNextRCache; + } + } + + // Throw away the old hash table. + + f_free( &ppOldHashTbl); + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: This routine changes the cache size for the record cache manager. + If necessary, it will resize the hash table. NOTE: This routine + assumes that the record cache mutex has been locked. +****************************************************************************/ +FSTATIC RCODE flmRcaSetMemLimit( + FLMUINT uiMaxCacheBytes) +{ + RCODE rc = FERR_OK; + + // If we are shrinking the maximum cache, clean up and + // defragment cache first + + gv_FlmSysData.RCacheMgr.Usage.uiMaxBytes = uiMaxCacheBytes; + if (gv_FlmSysData.RCacheMgr.pRCacheAlloc->getTotalBytesAllocated() > + uiMaxCacheBytes) + { + flmRcaCleanupCache( ~((FLMUINT)0), TRUE); + } + + // If the current record count is below the minimum records for the + // number of buckets or is greater than the maximum records for the + // number of buckets, we want to resize the hash table. + + if ((gv_FlmSysData.RCacheMgr.Usage.uiCount > + FLM_RCA_MAX_REC_CNT( gv_FlmSysData.RCacheMgr.uiNumBuckets) && + gv_FlmSysData.RCacheMgr.uiNumBuckets < MAX_RCACHE_BUCKETS) || + (gv_FlmSysData.RCacheMgr.Usage.uiCount < + FLM_RCA_MIN_REC_CNT( gv_FlmSysData.RCacheMgr.uiNumBuckets) && + gv_FlmSysData.RCacheMgr.uiNumBuckets > MIN_RCACHE_BUCKETS)) + { + if (RC_BAD( rc = flmRcaRehash())) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: This routine configures the record cache manager. NOTE: This routine + assumes that the record cache mutex has been locked. +****************************************************************************/ +RCODE flmRcaConfig( + FLMUINT uiType, + void * Value1, + void * Value2) +{ + RCODE rc = FERR_OK; + + F_UNREFERENCED_PARM( Value2); + + switch (uiType) + { + case FLM_CACHE_LIMIT: + rc = flmRcaSetMemLimit( (FLMUINT)Value1); + break; + case FLM_SCACHE_DEBUG: +#ifdef FLM_DEBUG + gv_FlmSysData.RCacheMgr.bDebug = (FLMBOOL)(Value1 ? + (FLMBOOL)TRUE : (FLMBOOL)FALSE); +#endif + break; + default: + rc = RC_SET( FERR_NOT_IMPLEMENTED); + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: This routine shuts down the record cache manager and frees all + resources allocated by it. +****************************************************************************/ +void flmRcaExit( void) +{ + if (gv_FlmSysData.RCacheMgr.hMutex != F_MUTEX_NULL) + { + f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); + } + + // Free all of the record cache objects. + + while (gv_FlmSysData.RCacheMgr.pMRURecord) + { + f_yieldCPU(); + flmRcaFreeCache( gv_FlmSysData.RCacheMgr.pMRURecord, FALSE); + } + + // Must free those in the purge list too. + + while (gv_FlmSysData.RCacheMgr.pPurgeList) + { + f_yieldCPU(); + flmRcaFreePurged( gv_FlmSysData.RCacheMgr.pPurgeList); + } + + // The math better be consistent! + + flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiCount == 0); + flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount == 0); + flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes == 0); + + // Free the hash bucket array + + if (gv_FlmSysData.RCacheMgr.ppHashBuckets) + { + f_free( &gv_FlmSysData.RCacheMgr.ppHashBuckets); + gv_FlmSysData.RCacheMgr.pRCacheAlloc->decrementTotalBytesAllocated( + (sizeof( RCACHE *) * gv_FlmSysData.RCacheMgr.uiNumBuckets)); + } + + // Free the mutex that controls access to record cache. + // NOTE: This should be done last so that the mutex is not + // unlocked until the very end. + + if (gv_FlmSysData.RCacheMgr.hMutex != F_MUTEX_NULL) + { + f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); + f_mutexDestroy( &gv_FlmSysData.RCacheMgr.hMutex); + } + + // Free the allocators + + if( gv_FlmSysData.RCacheMgr.pRecBufAlloc) + { + gv_FlmSysData.RCacheMgr.pRecBufAlloc->Release(); + gv_FlmSysData.RCacheMgr.pRecBufAlloc = NULL; + } + + if( gv_FlmSysData.RCacheMgr.pRecAlloc) + { + gv_FlmSysData.RCacheMgr.pRecAlloc->Release(); + gv_FlmSysData.RCacheMgr.pRecAlloc = NULL; + } + + if( gv_FlmSysData.RCacheMgr.pRCacheAlloc) + { + gv_FlmSysData.RCacheMgr.pRCacheAlloc->Release(); + gv_FlmSysData.RCacheMgr.pRCacheAlloc = NULL; + } + + // Zero the entire structure out, just for good measure. + + f_memset( &gv_FlmSysData.RCacheMgr, 0, sizeof( RCACHE_MGR)); +} + +/**************************************************************************** +Desc: This routine links a notify request into a notification list and + then waits to be notified that the event has occurred. + NOTE: This routine assumes that the record cache mutex is locked and that + it is supposed to unlock it. It will relock the mutex on its way out. +****************************************************************************/ +FSTATIC RCODE flmRcaWaitNotify( + FNOTIFY ** ppNotifyListRV) +{ + FNOTIFY * pNotify = NULL; + RCODE TempRc; + RCODE rc = FERR_OK; + F_SEM hSem; + + // First create a notify request and link it into the list. + + if (RC_OK( rc = f_calloc( (FLMUINT)(sizeof( FNOTIFY)), &pNotify))) + { + // Allocate a semaphore for the notify request. + + pNotify->uiThreadId = f_threadId(); + pNotify->hSem = F_SEM_NULL; + rc = f_semCreate( &pNotify->hSem); + } + + if (RC_BAD( rc)) + { + if (pNotify) + { + if (pNotify->hSem != F_SEM_NULL) + { + f_semDestroy( &pNotify->hSem); + } + + f_free( &pNotify); + } + + goto Exit; + } + + pNotify->pRc = &rc; + pNotify->UserData = NULL; + pNotify->pNext = *ppNotifyListRV; + *ppNotifyListRV = pNotify; + hSem = pNotify->hSem; + + // Unlock the mutex and wait on the semaphore. + + f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); + if (RC_BAD( TempRc = f_semWait( hSem, F_SEM_WAITFOREVER))) + { + rc = TempRc; + } + + // Free the semaphore and the notify structure. + + f_semDestroy( &hSem); + f_free( &pNotify); + + // Relock the record cache mutex + + f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: This routine notifies threads waiting for a pending read to complete. + NOTE: This routine assumes that the record cache mutex is already + locked. +****************************************************************************/ +FSTATIC void flmRcaNotify( + FNOTIFY * pNotify, + RCACHE * pUseRCache, + RCODE NotifyRc) +{ + while (pNotify) + { + F_SEM hSem; + + *(pNotify->pRc) = NotifyRc; + if (RC_OK( NotifyRc)) + { + RCA_INCR_USE_COUNT( pUseRCache->uiFlags); + } + hSem = pNotify->hSem; + pNotify = pNotify->pNext; + f_semSignal( hSem); + } +} + +/**************************************************************************** +Desc: Allocate a new record cache structure. This routine assumes that the + record cache mutex has already been locked. +****************************************************************************/ +FSTATIC RCODE flmRcaAllocCacheStruct( + RCACHE ** ppRCache) +{ + RCODE rc = FERR_OK; + + if( (*ppRCache = + (RCACHE *)gv_FlmSysData.RCacheMgr.pRCacheAlloc->allocCell()) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + f_memset( *ppRCache, 0, sizeof( RCACHE)); + + // Increment the total records cached + + gv_FlmSysData.RCacheMgr.Usage.uiCount++; + + // Set the high transaction ID to 0xFFFFFFFF so that this will NOT + // be treated as one that had memory assigned to the old records. + + (*ppRCache)->uiHighTransId = 0xFFFFFFFF; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Free a record cache structure. This routine assumes that the record + cache mutex has already been locked. +****************************************************************************/ +FSTATIC void flmRcaFreeCacheStruct( + RCACHE ** ppRCache) +{ + flmAssert( !RCA_IS_IN_HEAP_LIST( (*ppRCache)->uiFlags)); + + gv_FlmSysData.RCacheMgr.pRCacheAlloc->freeCell( *ppRCache); + *ppRCache = NULL; + + flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiCount > 0); + gv_FlmSysData.RCacheMgr.Usage.uiCount--; +} + +/**************************************************************************** +Desc: Cleanup old records in cache that are no longer needed by any + transaction. +****************************************************************************/ +void flmRcaCleanupCache( + FLMUINT uiMaxLockTime, + FLMBOOL bMutexesLocked) +{ + RCACHE * pTmpRCache; + RCACHE * pPrevRCache; + RCACHE * pNextRCache; + FLMUINT uiRecordsExamined = 0; + FLMUINT uiLastTimePaused = FLM_GET_TIMER(); + FLMUINT uiCurrTime; + FLMBOOL bUnlockMutexes = FALSE; + + if( !bMutexesLocked) + { + f_mutexLock( gv_FlmSysData.hShareMutex); + f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); + bUnlockMutexes = TRUE; + } + + // Try to free everything in the heap list + + pTmpRCache = gv_FlmSysData.RCacheMgr.pHeapList; + + while( pTmpRCache) + { + uiRecordsExamined++; + + // Save the pointer to the next entry in the list because + // we may end up unlinking pTmpRCache below + + pNextRCache = pTmpRCache->pNextInHeapList; + + // Determine if the item can be freed + + flmAssert( RCA_IS_IN_HEAP_LIST( pTmpRCache->uiFlags)); + + if( !RCA_IS_IN_USE( pTmpRCache->uiFlags) && + !RCA_IS_READING_IN( pTmpRCache->uiFlags)) + { + flmRcaFreeCache( pTmpRCache, FALSE); + } + + pTmpRCache = pNextRCache; + } + + // Now, free any old versions that are no longer needed + + flmRcaReduceCache( TRUE); + pTmpRCache = gv_FlmSysData.RCacheMgr.pLRURecord; + + // Stay in the loop until we have freed all old records, or + // we have run through the entire list. + + for( ;;) + { + if( !pTmpRCache || !gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes) + { + break; + } + + // After each 200 records examined, see if our maximum + // time has elapsed for examining without a pause. + + if( uiRecordsExamined >= 200) + { + uiRecordsExamined = 0; + uiCurrTime = FLM_GET_TIMER(); + + if( FLM_ELAPSED_TIME( uiCurrTime, uiLastTimePaused) >= uiMaxLockTime) + { + // IMPORTANT! Don't stop and pause on one that is + // being read in. + + while( pTmpRCache && RCA_IS_READING_IN( pTmpRCache->uiFlags)) + { + pTmpRCache = pTmpRCache->pPrevInGlobal; + } + + if( !pTmpRCache) + { + break; + } + + if( bUnlockMutexes) + { + // Increment the use count so that this item will not + // go away while we are paused. + + RCA_INCR_USE_COUNT( pTmpRCache->uiFlags); + f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); + f_mutexUnlock( gv_FlmSysData.hShareMutex); + + // Shortest possible pause - to allow other threads + // to do work. + + #ifdef FLM_NLM + f_yieldCPU(); + #else + f_sleep( 0); + #endif + + // Relock the mutexes + + uiLastTimePaused = FLM_GET_TIMER(); + f_mutexLock( gv_FlmSysData.hShareMutex); + f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); + + // Decrement use count that was added above. + + RCA_DECR_USE_COUNT( pTmpRCache->uiFlags); + } + + // If the item was purged while we were paused, + // finish the job and then start again at the + // top of the list. + + if( RCA_IS_PURGED( pTmpRCache->uiFlags)) + { + if( !RCA_IS_IN_USE( pTmpRCache->uiFlags)) + { + flmRcaFreePurged( pTmpRCache); + } + + pTmpRCache = gv_FlmSysData.RCacheMgr.pLRURecord; + continue; + } + } + } + + uiRecordsExamined++; + + // Save the pointer to the previous entry in the list because + // we may end up unlinking pTmpRCache below, in which case we would + // have lost the previous entry. + + pPrevRCache = pTmpRCache->pPrevInGlobal; + + // Block must not currently be in use, + // Must not be the most current version of a block, + // Cannot be dirty in any way, + // Cannot be in the process of being read in from disk, + // And must not be needed by a read transaction. + + if( !RCA_IS_IN_USE( pTmpRCache->uiFlags) && + !RCA_IS_READING_IN( pTmpRCache->uiFlags) && + (RCA_IS_IN_HEAP_LIST( pTmpRCache->uiFlags) || + (pTmpRCache->uiHighTransId != 0xFFFFFFFF && + !flmNeededByReadTrans( pTmpRCache->pFile, + pTmpRCache->uiLowTransId, + pTmpRCache->uiHighTransId)))) + { + flmRcaFreeCache( pTmpRCache, FALSE); + } + + pTmpRCache = pPrevRCache; + } + + // Defragment memory + + gv_FlmSysData.RCacheMgr.pRCacheAlloc->defragmentMemory(); + gv_FlmSysData.RCacheMgr.pRecAlloc->defragmentMemory(); + gv_FlmSysData.RCacheMgr.pRecBufAlloc->defragmentMemory(); + + // Unlock the mutexes + + if( bUnlockMutexes) + { + f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); + f_mutexUnlock( gv_FlmSysData.hShareMutex); + } +} + +/**************************************************************************** +Desc: This routine reduces record cache down to the limit expected. +****************************************************************************/ +void flmRcaReduceCache( + FLMBOOL bMutexAlreadyLocked) +{ + RCACHE * pRCache; + RCACHE * pPrevRCache; + + // Make sure the mutex is locked. + + if (!bMutexAlreadyLocked) + { + f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); + } + + pRCache = gv_FlmSysData.RCacheMgr.pLRURecord; + + // Free things until we get down below our memory limit. + + while( gv_FlmSysData.RCacheMgr.pRCacheAlloc->getTotalBytesAllocated() > + gv_FlmSysData.RCacheMgr.Usage.uiMaxBytes) + { + if( !pRCache) + { + break; + } + + // If the total of block and record cache is below the global + // cache maximum, there is no need to reduce record cache. + + if( (gv_FlmSysData.RCacheMgr.pRCacheAlloc->getTotalBytesAllocated() + + gv_FlmSysData.SCacheMgr.Usage.uiTotalBytesAllocated) <= + gv_FlmSysData.uiMaxCache) + { + break; + } + + pPrevRCache = pRCache->pPrevInGlobal; + if (!(RCA_IS_IN_USE( pRCache->uiFlags)) && + !(RCA_IS_READING_IN( pRCache->uiFlags))) + { + flmRcaFreeCache( pRCache, FALSE); + } + pRCache = pPrevRCache; + } + + if (!bMutexAlreadyLocked) + { + f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); + } +} + +/**************************************************************************** +Desc: This routine finds a record in the record cache. If it cannot + find the record, it will return the position where the record should + be inserted. NOTE: This routine assumes that the record cache mutex + has been locked. +****************************************************************************/ +void flmRcaFindRec( + FLMUINT uiContainer, + FLMUINT uiDrn, + FFILE_p pFile, + FLMUINT uiVersionNeeded, + FLMBOOL bDontPoisonCache, + FLMUINT * puiNumLooks, + RCACHE ** ppRCache, + RCACHE ** ppNewerRCache, + RCACHE ** ppOlderRCache) +{ + RCACHE * pRCache; + FLMUINT uiNumLooks = 0; + FLMBOOL bFound; + RCACHE * pNewerRCache; + RCACHE * pOlderRCache; + + // Search down the hash bucket for the matching item. + +Start_Find: + + // NOTE: Need to always calculate hash bucket because + // the hash table may have been changed while we + // were waiting to be notified below - mutex can + // be unlocked, but it is guaranteed to be locked + // here. + + pRCache = *(FLM_RCA_HASH( uiDrn)); + bFound = FALSE; + uiNumLooks = 1; + while ((pRCache) && + ((pRCache->uiDrn != uiDrn) || + (pRCache->uiContainer != uiContainer) || + (pRCache->pFile != pFile))) + { + if ((pRCache = pRCache->pNextInBucket) != NULL) + { + uiNumLooks++; + } + } + + // If we found the record, see if we have the right version. + + if (!pRCache) + { + pNewerRCache = pOlderRCache = NULL; + } + else + { + pNewerRCache = NULL; + pOlderRCache = pRCache; + for (;;) + { + + // If this one is being read in, we need to wait on it. + + if (RCA_IS_READING_IN( pRCache->uiFlags)) + { + // We need to wait for this record to be read in + // in case it coalesces with other versions, resulting + // in a version that satisfies our request. + + gv_FlmSysData.RCacheMgr.uiIoWaits++; + if (RC_BAD( flmRcaWaitNotify( &pRCache->pNotifyList))) + { + + // Don't care what error the other thread had reading + // the thing in from disk - we'll bail out and start + // our find again. + + goto Start_Find; + } + + // The thread doing the notify "uses" the record cache + // on behalf of this thread to prevent the record + // from being replaced after it unlocks the mutex. + // At this point, since we have locked the mutex, + // we need to release the record - because we + // will put a "use" on it below. + + RCA_DECR_USE_COUNT( pRCache->uiFlags); + + if (RCA_IS_PURGED( pRCache->uiFlags)) + { + if (!RCA_IS_IN_USE( pRCache->uiFlags)) + { + flmRcaFreePurged( pRCache); + } + } + + // Start over with the find because the list + // structure has changed. + + goto Start_Find; + } + + // See if this record version is the one we need. + + if (uiVersionNeeded < pRCache->uiLowTransId) + { + pNewerRCache = pRCache; + if ((pOlderRCache = pRCache = pRCache->pOlderVersion) == NULL) + { + break; + } + uiNumLooks++; + } + else if (uiVersionNeeded <= pRCache->uiHighTransId) + { + // Make this the MRU record. + + if (puiNumLooks) + { + if (bDontPoisonCache) + { + flmRcaStepUpInGlobalList( pRCache); + } + else if (pRCache->pPrevInGlobal) + { + flmRcaUnlinkFromGlobal( pRCache); + flmRcaLinkToGlobalAsMRU( pRCache); + } + + gv_FlmSysData.RCacheMgr.Usage.uiCacheHits++; + gv_FlmSysData.RCacheMgr.Usage.uiCacheHitLooks += uiNumLooks; + } + + bFound = TRUE; + break; + } + else + { + pOlderRCache = pRCache; + pNewerRCache = pRCache->pNewerVersion; + + // Set pRCache to NULL as an indicator that we did not + // find the version we needed. + + pRCache = NULL; + break; + } + } + } + + *ppRCache = pRCache; + *ppOlderRCache = pOlderRCache; + *ppNewerRCache = pNewerRCache; + + if (puiNumLooks) + { + *puiNumLooks = uiNumLooks; + } +} + +/**************************************************************************** +Desc: This routine replaces the FlmRecord that a record is pointing to + with a new one. NOTE: This routine assumes that the record cache + mutex is already locked. +****************************************************************************/ +FSTATIC void flmRcaSetRecord( + RCACHE * pRCache, + FlmRecord * pNewRecord) +{ + FLMUINT uiTotalMemory = 0; + FLMUINT uiFreeMemory; + FlmRecord * pOldRecord; + + // Release the cache's pointer to the old record data + + if ((pOldRecord = pRCache->pRecord) != NULL) + { + if( RCA_IS_IN_HEAP_LIST( pRCache->uiFlags)) + { + flmRcaUnlinkFromHeapList( pRCache); + } + + uiTotalMemory = pOldRecord->getTotalMemory(); + uiFreeMemory = pOldRecord->getFreeMemory(); + flmAssert( uiTotalMemory >= uiFreeMemory); + FlmRecordExt::clearCached( pOldRecord); + FlmRecordExt::Release( pOldRecord, TRUE); + pRCache->pRecord = NULL; + } + + if (pRCache->uiHighTransId != 0xFFFFFFFF) + { + FLMUINT uiTotalOldMemory = uiTotalMemory + sizeof( RCACHE); + + flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes >= + uiTotalOldMemory); + flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount); + gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes -= + uiTotalOldMemory; + gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount--; + } + + // Point to the new record data. + + flmAssert( pNewRecord->getID() == pRCache->uiDrn); + flmAssert( pNewRecord->getContainerID() == pRCache->uiContainer); + pRCache->pRecord = pNewRecord; + flmAssert( !pNewRecord->isCached()); + FlmRecordExt::setCached( pNewRecord); + pNewRecord->AddRef(); + FlmRecordExt::setReadOnly( pNewRecord); + + if( FlmRecordExt::getFlags( pNewRecord) & RCA_HEAP_BUFFER) + { + flmRcaLinkToHeapList( pRCache); + } + + uiTotalMemory = pNewRecord->getTotalMemory(); + + if (pRCache->uiHighTransId != 0xFFFFFFFF) + { + gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes += + (uiTotalMemory + sizeof( RCACHE)); + gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount++; + } + uiFreeMemory = pNewRecord->getFreeMemory(); + flmAssert( uiTotalMemory >= uiFreeMemory); +} + +/**************************************************************************** +Desc: This routine links a new RCACHE structure into the global list and + into the correct place in its hash bucket. This routine assumes that + the record cache mutex is already locked. +****************************************************************************/ +FSTATIC void flmRcaLinkIntoRCache( + RCACHE * pNewerRCache, + RCACHE * pOlderRCache, + RCACHE * pRCache, + FLMBOOL bLinkAsMRU) +{ + if( bLinkAsMRU) + { + flmRcaLinkToGlobalAsMRU( pRCache); + } + else + { + flmRcaLinkToGlobalAsLRU( pRCache); + } + + if (pNewerRCache) + { + flmRcaLinkToVerList( pRCache, pNewerRCache, pOlderRCache); + } + else + { + RCACHE * pNull = NULL; + + if (pOlderRCache) + { + flmRcaUnlinkFromHashBucket( pOlderRCache); + } + flmRcaLinkToHashBucket( pRCache); + flmRcaLinkToVerList( pRCache, pNull, pOlderRCache); + } +} + +/**************************************************************************** +Desc: This routine links a new record to its FFILE according to whether + or not it is an update transaction or a read transaction. + It coalesces out any unnecessary versions. This routine assumes + that the record cache mutex is already locked. +****************************************************************************/ +FSTATIC void flmRcaLinkToFFILE( + RCACHE * pRCache, + FFILE_p pFile, + FDB_p pDb, + FLMUINT uiLowTransId, + FLMBOOL bMostCurrent) +{ + RCACHE * pTmpRCache; +#ifdef FLM_DBG_LOG + char szTmpBuf[ 80]; +#endif + + + pRCache->uiLowTransId = uiLowTransId; + + // Before coalescing, link to FFILE. + // The following test determines if the record is an + // uncommitted version generated by the update transaction. + // If so, we mark it as such, and link it at the head of the + // FFILE list - so we can get rid of it quickly if we abort + // the transaction. + + if (flmGetDbTransType( pDb) == FLM_UPDATE_TRANS) + { + + // If we are in an update transaction, there better not + // be any newer versions in the list and the high + // transaction ID returned better be 0xFFFFFFFF. + + flmAssert( pRCache->pNewerVersion == NULL); + flmRcaSetTransID( pRCache, 0xFFFFFFFF); + + // If the low transaction ID is the same as the transaction, + // we may have modified this record during the transaction. + // Unfortunately, there is no sure way to tell, so we are + // forced to assume it may have been modified. If the + // transaction aborts, we will get rid if this version out + // of cache. + + if (uiLowTransId == pDb->LogHdr.uiCurrTransID) + { + RCA_SET_UNCOMMITTED( pRCache->uiFlags); + flmRcaLinkToFileAtHead( pRCache, pFile); + } + else + { + RCA_UNSET_UNCOMMITTED( pRCache->uiFlags); + flmRcaLinkToFileAtEnd( pRCache, pFile); + } +#ifdef FLM_DBG_LOG + f_sprintf( szTmpBuf, "RCI:L%X,H%X", + (unsigned)pRCache->uiLowTransId, + (unsigned)pRCache->uiHighTransId); + + flmDbgLogWrite( pFile->uiFFileId, pRCache->uiContainer, pRCache->uiDrn, + pDb->LogHdr.uiCurrTransID, szTmpBuf); +#endif + } + else + { + // Adjust the high transaction ID to be the same as + // the transaction ID - we may have gotten a 0xFFFFFFF + // back, but that is possible even if the record is + // not the most current version. Besides that, it is + // possible that in the mean time one or more update + // transactions have come along and created one or + // more newer versions of the record. + + FLMUINT uiHighTransId = (FLMUINT)((bMostCurrent) + ? (FLMUINT)0xFFFFFFFF + : pDb->LogHdr.uiCurrTransID); + + flmRcaSetTransID( pRCache, uiHighTransId); + + // For a read transaction, if there is a newer version, + // it better have a higher "low transaction ID" + +#ifdef FLM_DBG_LOG + f_sprintf( szTmpBuf, "RCA:L%X,H%X", + (unsigned)pRCache->uiLowTransId, + (unsigned)pRCache->uiHighTransId); + + flmDbgLogWrite( pFile->uiFFileId, pRCache->uiContainer, pRCache->uiDrn, + pDb->LogHdr.uiCurrTransID, szTmpBuf); +#endif + +#ifdef FLM_DEBUG + if (pRCache->pNewerVersion && + !RCA_IS_READING_IN( pRCache->pNewerVersion->uiFlags)) + { + flmAssert( pRCache->uiHighTransId < + pRCache->pNewerVersion->uiLowTransId); + if( pRCache->uiHighTransId >= + pRCache->pNewerVersion->uiLowTransId) + { + flmRcaCheck( pDb, pRCache->uiContainer, pRCache->uiDrn); + } + } +#endif + RCA_UNSET_UNCOMMITTED( pRCache->uiFlags); + flmRcaLinkToFileAtEnd( pRCache, pFile); + } + + // Coalesce any versions that overlap - can only + // coalesce older versions. For an updater, there + // should not be any newer versions. For a reader, it + // is impossible to know how high up it can coalesce. + // The read operation that read the record may have + // gotten back a 0xFFFFFFFF for its high transaction + // ID - but after that point in time, it is possible + // that one or more update transactions may have come + // along and created one or more newer versions that + // it would be incorrect to coalesce with. + // In reality, a read transaction has to ignore the + // 0xFFFFFFFF in the high transaction ID anyway + // because there is no way to know if it is correct. + + // Coalesce older versions. + + for (;;) + { + if ((pTmpRCache = pRCache->pOlderVersion) == NULL) + { + break; + } + + // Stop if we encounter one that is being read in. + + if (RCA_IS_READING_IN( pTmpRCache->uiFlags)) + { + break; + } + + // If there is no overlap between these two, there is + // nothing more to coalesce. + + if (pRCache->uiLowTransId > pTmpRCache->uiHighTransId) + { + break; + } + + if (pRCache->uiHighTransId <= pTmpRCache->uiHighTransId) + { + // This assert represents the following case, + // which should not be possible to hit: + + // pOlder->uiHighTransId > pRCache->uiHighTransId. + // This cannot be, because if pOlder has a higher + // transaction ID, we would have found it up above and + // not tried to have read it in. + + flmAssert( 0); +#ifdef FLM_DEBUG + flmRcaCheck( pDb, pRCache->uiContainer, pRCache->uiDrn); +#endif + } + else if (pRCache->uiLowTransId >= pTmpRCache->uiLowTransId) + { + pRCache->uiLowTransId = pTmpRCache->uiLowTransId; + flmRcaFreeCache( pTmpRCache, + (FLMBOOL)((RCA_IS_IN_USE( pTmpRCache->uiFlags) || + RCA_IS_READING_IN( pTmpRCache->uiFlags)) + ? TRUE + : FALSE)); + } + else + { + // This assert represents the following case, + // which should not be possible to hit: + + // pRCache->uiLowTransId < pOlder->uiLowTransId. + // This cannot be, because pOlder has to have been read + // in to memory by a transaction whose transaction ID is + // less than or equal to our own. That being the case, + // it would be impossible for our transaction to have + // found a version of the record that is older than pOlder. + + flmAssert( 0); +#ifdef FLM_DEBUG + flmRcaCheck( pDb, pRCache->uiContainer, pRCache->uiDrn); +#endif + } + } +} + +/**************************************************************************** +Desc: This routine retrieves a record from the record cache. +****************************************************************************/ +RCODE flmRcaRetrieveRec( + FDB * pDb, + FLMBOOL * pbTransStarted, + FLMUINT uiContainer, // Container record is in. + FLMUINT uiDrn, // DRN of record. + FLMBOOL bOkToGetFromDisk, // If not in cache, OK to get from disk? + BTSK * pStack, // Use stack to retrieve, if NON-NULL. + LFILE * pLFile, // LFILE to use, if retrieving with stack + FlmRecord ** ppRecord) +{ + RCODE rc = FERR_OK; + FLMBOOL bRCacheMutexLocked = FALSE; + FFILE_p pFile = pDb->pFile; + RCACHE * pRCache; + RCACHE * pNewerRCache; + RCACHE * pOlderRCache; + FlmRecord * pRecord = NULL; + FLMBOOL bGotFromDisk = FALSE; + FlmRecord * pNewRecord = NULL; + FlmRecord * pOldRecord = NULL; + FLMUINT uiLowTransId; + FLMBOOL bMostCurrent; + FLMUINT uiCurrTransId; + FNOTIFY_p pNotify; + FLMUINT uiNumLooks; + FLMBOOL bInitializedFdb = FALSE; + FLMBOOL bDontPoisonCache = pDb->uiFlags & FDB_DONT_POISON_CACHE + ? TRUE + : FALSE; + + if( RC_BAD( rc = flmCheckDatabaseState( pDb))) + { + goto Exit; + } + + // Get the current transaction ID + + if( pDb->uiTransType != FLM_NO_TRANS) + { + uiCurrTransId = pDb->LogHdr.uiCurrTransID; + } + else + { + flmAssert( pbTransStarted != NULL); + + f_mutexLock( gv_FlmSysData.hShareMutex); + + // Get the last committed transaction ID. + + uiCurrTransId = (FLMUINT)FB2UD( + &pFile->ucLastCommittedLogHdr[ LOG_CURR_TRANS_ID]); + + f_mutexUnlock( gv_FlmSysData.hShareMutex); + } + + flmAssert( uiDrn != 0); + + // Lock the mutex + + f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); + bRCacheMutexLocked = TRUE; + + // Reset the FDB's inactive time + + pDb->uiInactiveTime = 0; + + // See if we should resize the hash table. + + if ((gv_FlmSysData.RCacheMgr.Usage.uiCount > + FLM_RCA_MAX_REC_CNT( gv_FlmSysData.RCacheMgr.uiNumBuckets) && + gv_FlmSysData.RCacheMgr.uiNumBuckets < MAX_RCACHE_BUCKETS) || + (gv_FlmSysData.RCacheMgr.Usage.uiCount < + FLM_RCA_MIN_REC_CNT( gv_FlmSysData.RCacheMgr.uiNumBuckets) && + gv_FlmSysData.RCacheMgr.uiNumBuckets > MIN_RCACHE_BUCKETS)) + { + if (RC_BAD( rc = flmRcaRehash())) + { + goto Exit; + } + } + +Start_Find: + + flmRcaFindRec( uiContainer, uiDrn, pFile, + uiCurrTransId, bDontPoisonCache, + &uiNumLooks, &pRCache, + &pNewerRCache, &pOlderRCache); + + if (pRCache) + { + if( ppRecord) + { + goto Found_Record; + } + goto Exit; + } + + // Did not find the record, fetch from disk, if OK to do so. + + if (!bOkToGetFromDisk || !ppRecord) + { + rc = RC_SET( FERR_NOT_FOUND); + goto Exit; + } + + // Code to handle case where we are not in a transaction. + // If we are already in a transaction, we will do the + // call to fdbInit below - AFTER allocating the RCACHE, etc. + + if( pbTransStarted && pDb->uiTransType == FLM_NO_TRANS) + { + f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); + bRCacheMutexLocked = FALSE; + + if ( RC_BAD( rc = fdbInit( pDb, FLM_READ_TRANS, + FDB_TRANS_GOING_OK, 0, pbTransStarted))) + { + fdbExit( pDb); + goto Exit; + } + bInitializedFdb = TRUE; + + f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); + bRCacheMutexLocked = TRUE; + + uiCurrTransId = pDb->LogHdr.uiCurrTransID; + goto Start_Find; + } + + // Increment the number of faults only if we retrieve the record from disk. + + gv_FlmSysData.RCacheMgr.Usage.uiCacheFaults++; + gv_FlmSysData.RCacheMgr.Usage.uiCacheFaultLooks += uiNumLooks; + + // Create a place holder for the object. + + if (RC_BAD( rc = flmRcaAllocCacheStruct( &pRCache))) + { + goto Exit; + } + pRCache->uiDrn = uiDrn; + pRCache->uiContainer = uiContainer; + + // Set the FFILE so that other threads looking for this record in + // cache will find it and wait until the read has completed. If + // the FFILE is not set, other threads will attempt their own read, + // because they won't match a NULL FFILE. The result of not setting + // the FFILE is that multiple copies of the same version of a particular + // record could end up in cache. + pRCache->pFile = pFile; + + flmRcaLinkIntoRCache( pNewerRCache, pOlderRCache, + pRCache, !bDontPoisonCache); + + RCA_SET_READING_IN( pRCache->uiFlags); + RCA_INCR_USE_COUNT( pRCache->uiFlags); + pRCache->pNotifyList = NULL; + + // Unlock mutex before reading in from disk. + + f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); + bRCacheMutexLocked = FALSE; + + if( pbTransStarted && !bInitializedFdb) + { + if ( RC_BAD( rc = fdbInit( pDb, FLM_READ_TRANS, + FDB_TRANS_GOING_OK, 0, pbTransStarted))) + { + fdbExit( pDb); + goto Notify_Waiters; + } + bInitializedFdb = TRUE; + } + + // Read record from disk. + +#ifdef FLM_DBG_LOG + flmDbgLogWrite( pFile->uiFFileId, uiContainer, + uiDrn, pDb->LogHdr.uiCurrTransID, "RRD"); +#endif + + if (pStack) + { + rc = FSReadElement( pDb, &pDb->TempPool, pLFile, + uiDrn, pStack, TRUE, ppRecord, + &uiLowTransId, &bMostCurrent); + } + else if ((pLFile) || + (RC_OK( rc = fdictGetContainer( pDb->pDict, uiContainer, &pLFile)))) + { + rc = FSReadRecord( pDb, pLFile, uiDrn, ppRecord, + &uiLowTransId, &bMostCurrent); + } + +Notify_Waiters: + + // Relock mutex + + f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); + bRCacheMutexLocked = TRUE; + + // If read was successful, link the record to its place in + // the FFILE list and coalesce any versions that overlap + // this one. + + if (RC_OK( rc)) + { + flmRcaLinkToFFILE( pRCache, + pDb->pFile, pDb, uiLowTransId, bMostCurrent); + } + + RCA_UNSET_READING_IN( pRCache->uiFlags); + + // Notify any threads waiting for the read to complete. + + pNotify = pRCache->pNotifyList; + pRCache->pNotifyList = NULL; + if (pNotify) + { + flmRcaNotify( pNotify, + (RCACHE *)((RC_BAD( rc)) + ? NULL + : pRCache), rc); + } + RCA_DECR_USE_COUNT( pRCache->uiFlags); + + // If we did not succeed, free the RCACHE structure. + + if (RC_BAD( rc)) + { + flmRcaFreeCache( pRCache, FALSE); + goto Exit; + } + + // If this item was purged while we were reading it in, + // start over with the search. + + if (RCA_IS_PURGED( pRCache->uiFlags)) + { + if (!RCA_IS_IN_USE( pRCache->uiFlags)) + { + flmRcaFreePurged( pRCache); + } + + // Start over with the find - this one has + // been marked for purging. + + pNewRecord = NULL; + pOldRecord = NULL; + bGotFromDisk = FALSE; + goto Start_Find; + } + + // When reading from disk, no need to verify that we + // are getting the app implementation - because we + // always will. + + bGotFromDisk = TRUE; + pRecord = *ppRecord; + flmRcaSetRecord( pRCache, pRecord); + +Found_Record: + + if (!bGotFromDisk) + { + if( (pRecord = *ppRecord) != pRCache->pRecord) + { + if (*ppRecord) + { + FlmRecordExt::Release( *ppRecord, bRCacheMutexLocked); + } + + pRecord = *ppRecord = pRCache->pRecord; + pRecord->AddRef(); + } + } + + // Clean up cache, if necessary. + + if (gv_FlmSysData.RCacheMgr.pRCacheAlloc->getTotalBytesAllocated() > + gv_FlmSysData.RCacheMgr.Usage.uiMaxBytes) + { + flmRcaReduceCache( bRCacheMutexLocked); + } + +Exit: + + if (pNewRecord) + { + FlmRecordExt::Release( pNewRecord, bRCacheMutexLocked); + } + + if (bRCacheMutexLocked) + { + f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); + } + + if (bInitializedFdb) + { + fdbExit(pDb); + } + + return( rc); +} + +/**************************************************************************** +Desc: This routine inserts a record into the record cache. This is ONLY + called by FlmRecordModify or FlmRecordAdd. In the case of a modify, + this should replace any uncommitted version of the record that may + have been put there by a prior call to FlmRecordModify. +****************************************************************************/ +RCODE flmRcaInsertRec( + FDB * pDb, + FLMUINT uiContainer, // Container record is in. + FLMUINT uiDrn, // DRN of record + FlmRecord * pRecord) // Record to be inserted. +{ + RCODE rc = FERR_OK; + FFILE_p pFile = pDb->pFile; + FLMBOOL bMutexLocked = FALSE; + RCACHE * pRCache; + RCACHE * pNewerRCache; + RCACHE * pOlderRCache; + FLMBOOL bDontPoisonCache = pDb->uiFlags & FDB_DONT_POISON_CACHE + ? TRUE + : FALSE; + + flmAssert( uiDrn != 0); + + // Lock the mutex + + f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); + bMutexLocked = TRUE; + + if ((gv_FlmSysData.RCacheMgr.Usage.uiCount > + FLM_RCA_MAX_REC_CNT( gv_FlmSysData.RCacheMgr.uiNumBuckets) && + gv_FlmSysData.RCacheMgr.uiNumBuckets < MAX_RCACHE_BUCKETS) || + (gv_FlmSysData.RCacheMgr.Usage.uiCount < + FLM_RCA_MIN_REC_CNT( gv_FlmSysData.RCacheMgr.uiNumBuckets) && + gv_FlmSysData.RCacheMgr.uiNumBuckets > MIN_RCACHE_BUCKETS)) + { + if (RC_BAD( rc = flmRcaRehash())) + { + goto Exit; + } + } + + // See if we can find the record in cache + + flmRcaFindRec( uiContainer, uiDrn, pFile, + pDb->LogHdr.uiCurrTransID, bDontPoisonCache, NULL, &pRCache, + &pNewerRCache, &pOlderRCache); + + if (pRCache) + { + + // If we found the last committed version, instead of replacing it, + // we want to change its high transaction ID, and go create a new + // record to put in cache. + + if (pRCache->uiLowTransId < pDb->LogHdr.uiCurrTransID) + { + + // pOlderRCache and pRCache should be the same at this point if we + // found something. Furthermore, the high transaction ID on what + // we found better be -1 - most current version. + + flmAssert( pOlderRCache == pRCache); + flmAssert( pOlderRCache->uiHighTransId == 0xFFFFFFFF); + + flmRcaSetTransID( pOlderRCache, (pDb->LogHdr.uiCurrTransID - 1)); + + flmAssert( pOlderRCache->uiHighTransId >= pOlderRCache->uiLowTransId); + + RCA_SET_UNCOMMITTED( pOlderRCache->uiFlags); + RCA_SET_LATEST_VER( pOlderRCache->uiFlags); + flmRcaUnlinkFromFile( pOlderRCache); + flmRcaLinkToFileAtHead( pOlderRCache, pFile); + } + else + { + // Found latest UNCOMMITTED VERSION - replace it. + +#ifdef FLM_CHECK_RECORD + if (RC_BAD( rc = pRecord->checkRecord())) + { + goto Exit; + } +#endif + + if (RC_BAD( rc = pRecord->compressMemory())) + { + goto Exit; + } + +#ifdef FLM_CHECK_RECORD + if (RC_BAD( rc = pRecord->checkRecord())) + { + goto Exit; + } +#endif + + // Replace the old record data with the new record data. + + flmRcaSetRecord( pRCache, pRecord); + + // Make sure we set the "uncommitted" flag and move the record + // to the head of the FFILE's list. + + if (!RCA_IS_UNCOMMITTED( pRCache->uiFlags)) + { + RCA_SET_UNCOMMITTED( pRCache->uiFlags); + flmRcaUnlinkFromFile( pRCache); + flmRcaLinkToFileAtHead( pRCache, pFile); + } + + // Will not have already been put at MRU if bDonPoisonCache is TRUE. + + if (pRCache->pPrevInGlobal) + { + flmRcaUnlinkFromGlobal( pRCache); + flmRcaLinkToGlobalAsMRU( pRCache); + } + + goto Exit; + } + } + + // We are positioned to insert the new record. For an update, it + // must always be the newest version. + + flmAssert( pNewerRCache == NULL); + +#ifdef FLM_CHECK_RECORD + if (RC_BAD( rc = pRecord->checkRecord())) + { + goto Exit; + } +#endif + + if (RC_BAD( rc = pRecord->compressMemory())) + { + goto Exit; + } + +#ifdef FLM_CHECK_RECORD + if (RC_BAD( rc = pRecord->checkRecord())) + { + goto Exit; + } +#endif + + // Allocate a new RCACHE structure. + + if (RC_BAD( rc = flmRcaAllocCacheStruct( &pRCache))) + { + goto Exit; + } + + // Set the DRN and container for the structure. + + pRCache->uiDrn = uiDrn; + pRCache->uiContainer = uiContainer; + pRCache->pFile = pFile; + + // Link into the global list and hash bucket. + + flmRcaLinkIntoRCache( pNewerRCache, pOlderRCache, pRCache, TRUE); + + // Link to its FFILE and coalesce out duplicates. + // NOTE: This routine automatically puts an uncommitted version + // at the head of the FFILE's list and sets the uncommitted flag. + + flmRcaLinkToFFILE( pRCache, pFile, pDb, pDb->LogHdr.uiCurrTransID, TRUE); + + // Set the record data pointer for the RCACHE structure. This will also + // release the old data pointer and set the read only flag and the mutex + // for the record data. Also updates memory usage fields in RCacheMgr. + + flmRcaSetRecord( pRCache, pRecord); + + // Clean up cache, if necessary. + + flmRcaReduceCache( bMutexLocked); + +Exit: + + if (bMutexLocked) + { + f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); + } + + return( rc); +} + +/**************************************************************************** +Desc: This routine is called by FlmRecordDelete to remove a record from + cache. If there is an uncommitted version of the record, it should + remove that version from cache. If the last committed version is in + cache, it should set the high transaction ID on that version to be + one less than the transaction ID of the update transaction that is + doing the FlmRecordDelete call. +****************************************************************************/ +RCODE flmRcaRemoveRec( + FDB * pDb, + FLMUINT uiContainer, + FLMUINT uiDrn) +{ + RCODE rc = FERR_OK; + FLMBOOL bMutexLocked = FALSE; + RCACHE * pRCache; + RCACHE * pNewerRCache; + RCACHE * pOlderRCache; + FFILE_p pFile = pDb->pFile; + + flmAssert( uiDrn != 0); + + // Lock the semaphore + + bMutexLocked = TRUE; + f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); + + if ((gv_FlmSysData.RCacheMgr.Usage.uiCount > + FLM_RCA_MAX_REC_CNT( gv_FlmSysData.RCacheMgr.uiNumBuckets) && + gv_FlmSysData.RCacheMgr.uiNumBuckets < MAX_RCACHE_BUCKETS) || + (gv_FlmSysData.RCacheMgr.Usage.uiCount < + FLM_RCA_MIN_REC_CNT( gv_FlmSysData.RCacheMgr.uiNumBuckets) && + gv_FlmSysData.RCacheMgr.uiNumBuckets > MIN_RCACHE_BUCKETS)) + { + if (RC_BAD( rc = flmRcaRehash())) + { + goto Exit; + } + } + + // See if we can find the record in cache + + flmRcaFindRec( uiContainer, uiDrn, pFile, + pDb->LogHdr.uiCurrTransID, FALSE, NULL, &pRCache, + &pNewerRCache, &pOlderRCache); + + if (pRCache) + { + // FlmRecordDelete is calling this routine, so we determine if we found + // the last committed version or a record that was added by this same + // transaction. If we found the last committed version, set its high + // transaction ID. Otherwise, remove the record from cache. + + if (pRCache->uiLowTransId < pDb->LogHdr.uiCurrTransID) + { + + // pOlderRCache and pRCache should be the same at this point if we + // found something. Furthermore, the high transaction ID on what + // we found better be -1 - most current version. + + flmAssert( pOlderRCache == pRCache); + flmAssert( pOlderRCache->uiHighTransId == 0xFFFFFFFF); + + flmRcaSetTransID( pOlderRCache, (pDb->LogHdr.uiCurrTransID - 1)); + flmAssert( pOlderRCache->uiHighTransId >= pOlderRCache->uiLowTransId); + RCA_SET_UNCOMMITTED( pOlderRCache->uiFlags); + RCA_SET_LATEST_VER( pOlderRCache->uiFlags); + flmRcaUnlinkFromFile( pOlderRCache); + flmRcaLinkToFileAtHead( pOlderRCache, pFile); + } + else + { + flmRcaFreeCache( pRCache, + (FLMBOOL)(RCA_IS_IN_USE( pRCache->uiFlags) + ? TRUE + : FALSE)); + } + } + + // Take the opportunity to clean up cache. + + flmRcaReduceCache( bMutexLocked); + +Exit: + + if (bMutexLocked) + { + f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); + } + + return( rc); +} + +/**************************************************************************** +Desc: This routine is called when an FFILE structure is going to be removed + from the shared memory area. At that point, we also need to get rid + of all records that have been cached for that FFILE. +****************************************************************************/ +void flmRcaFreeFileRecs( + FFILE_p pFile) +{ + FLMUINT uiNumFreed = 0; + + f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); + while (pFile->pFirstRecord) + { + flmRcaFreeCache( pFile->pFirstRecord, + RCA_IS_IN_USE( pFile->pFirstRecord->uiFlags) + ? TRUE + : FALSE); + + // Release the CPU every 100 records freed. + + if (uiNumFreed < 100) + { + uiNumFreed++; + } + else + { + f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); + f_yieldCPU(); + f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); + uiNumFreed = 0; + } + } + f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); +} + +/**************************************************************************** +Desc: This routine is called when an update transaction aborts. At that + point, we need to get rid of any uncommitted versions of records in + the record cache. +****************************************************************************/ +void flmRcaAbortTrans( + FDB * pDb + ) +{ + FFILE_p pFile = pDb->pFile; + RCACHE * pRCache; + RCACHE * pOlderVersion; + FLMUINT uiOlderTransId = + pDb->LogHdr.uiCurrTransID - 1; + + f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); + pRCache = pFile->pFirstRecord; + while (pRCache) + { + if (RCA_IS_UNCOMMITTED( pRCache->uiFlags)) + { + if (RCA_IS_LATEST_VER( pRCache->uiFlags)) + { + flmRcaSetTransID( pRCache, 0xFFFFFFFF); + RCA_UNSET_UNCOMMITTED( pRCache->uiFlags); + RCA_UNSET_LATEST_VER( pRCache->uiFlags); + flmRcaUnlinkFromFile( pRCache); + flmRcaLinkToFileAtEnd( pRCache, pFile); + } + else + { + // Save the older version - we may be changing its + // high transaction ID back to 0xFFFFFFFF + + pOlderVersion = pRCache->pOlderVersion; + + // Free the uncommitted version. + + flmRcaFreeCache( pRCache, + (FLMBOOL)((RCA_IS_IN_USE( pRCache->uiFlags) || + RCA_IS_READING_IN( pRCache->uiFlags)) + ? TRUE + : FALSE)); + + // If the older version has a high transaction ID that + // is exactly one less than our current transaction, + // it is the most current version. Hence, we need to + // change its high transaction ID back to 0xFFFFFFFF. + + if ((pOlderVersion) && + (pOlderVersion->uiHighTransId == uiOlderTransId)) + { + flmRcaSetTransID( pOlderVersion, 0xFFFFFFFF); + } + } + pRCache = pFile->pFirstRecord; + } + else + { + // We can stop when we hit a committed version, because + // uncommitted versions are always linked in together at + // the head of the list. + + break; + } + } + f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); +} + +/**************************************************************************** +Desc: This routine is called when an update transaction commits. At that + point, we need to unset the "uncommitted" flag on any records + currently in record cache for the FFILE. +****************************************************************************/ +void flmRcaCommitTrans( + FDB * pDb) +{ + RCACHE * pRCache; + + f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); + pRCache = pDb->pFile->pFirstRecord; + while (pRCache) + { + if (RCA_IS_UNCOMMITTED( pRCache->uiFlags)) + { + RCA_UNSET_UNCOMMITTED( pRCache->uiFlags); + RCA_UNSET_LATEST_VER( pRCache->uiFlags); + pRCache = pRCache->pNextInFile; + } + else + { + + // We can stop when we hit a committed version, because + // uncommitted versions are always linked in together at + // the head of the list. + + break; + } + } + f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); +} + +/**************************************************************************** +Desc: This routine is called when a container in the database is deleted. + All records in record cache that are in that container must be + removed from cache. +****************************************************************************/ +void flmRcaRemoveContainerRecs( + FDB * pDb, + FLMUINT uiContainer) +{ + FFILE * pFile = pDb->pFile; + RCACHE * pRCache; + RCACHE * pPrevRCache; + FLMUINT uiTransId = pDb->LogHdr.uiCurrTransID; + + f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); + pRCache = gv_FlmSysData.RCacheMgr.pLRURecord; + + // Stay in the loop until we have freed all old records, or + // we have run through the entire list. + + while (pRCache) + { + // Save the pointer to the previous entry in the list because + // we may end up unlinking pRCache below, in which case we would + // have lost the previous entry. + + pPrevRCache = pRCache->pPrevInGlobal; + + // Only look at records in this container and this database. + + if ((pRCache->uiContainer == uiContainer) && + (pRCache->pFile == pFile)) + { + + // Only look at the most current versions. + + if (pRCache->uiHighTransId == 0xFFFFFFFF) + { + + // Better not be a newer version. + + flmAssert( pRCache->pNewerVersion == NULL); + + if (pRCache->uiLowTransId < uiTransId) + { + + // This version was not added or modified by this + // transaction so it's high transaction ID should simply + // be set to one less than the current transaction ID. + + flmRcaSetTransID( pRCache, uiTransId - 1); + flmAssert( pRCache->uiHighTransId >= pRCache->uiLowTransId); + RCA_SET_UNCOMMITTED( pRCache->uiFlags); + RCA_SET_LATEST_VER( pRCache->uiFlags); + flmRcaUnlinkFromFile( pRCache); + flmRcaLinkToFileAtHead( pRCache, pFile); + } + else + { + + // The record was added or modified in this + // transaction. Simply remove it from cache. + + flmRcaFreeCache( pRCache, + (FLMBOOL)(RCA_IS_IN_USE( pRCache->uiFlags) + ? TRUE + : FALSE)); + } + } + else + { + + // If not most current version, the record's high transaction + // ID better already be less than transaction ID. + + flmAssert( pRCache->uiHighTransId < uiTransId); + } + } + pRCache = pPrevRCache; + + } + f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +#ifdef FLM_DEBUG +FSTATIC RCODE flmRcaCheck( + FDB_p pDb, + FLMUINT uiContainer, + FLMUINT uiDrn) +{ + LFILE * pLFile; + FlmRecord * pRecord = NULL; + FLMUINT uiLowTransId; + FLMBOOL bMostCurrent; + RCODE rc; + + if( RC_OK( rc = fdictGetContainer( pDb->pDict, uiContainer, &pLFile))) + { + rc = FSReadRecord( pDb, pLFile, uiDrn, &pRecord, + &uiLowTransId, &bMostCurrent); + } + + if( pRecord) + { + pRecord->Release(); + } + + return( rc); +} +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMBOOL FlmRecordExt::canRelocateRec( + void * pvAlloc) +{ + FlmRecord * pRec = (FlmRecord *)pvAlloc; + + if( pRec->getRefCount() == 1 && pRec->isCached()) + { + return( TRUE); + } + + return( FALSE); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void FlmRecordExt::relocateRec( + void * pvOldAlloc, + void * pvNewAlloc) +{ + FlmRecord * pOldRec = (FlmRecord *)pvOldAlloc; + FlmRecord * pNewRec = (FlmRecord *)pvNewAlloc; + RCACHE * pRCache; + RCACHE * pVersion; + + flmAssert( pOldRec->getRefCount() == 1); + flmAssert( pOldRec->isCached()); + flmAssert( pvNewAlloc < pvOldAlloc); + + // Update the record pointer in the data buffer + + if( pNewRec->m_pucBuffer) + { + flmAssert( *((FlmRecord **)pOldRec->m_pucBuffer) == pOldRec); + *((FlmRecord **)pNewRec->m_pucBuffer) = pNewRec; + } + + // Find the record + + pRCache = *(FLM_RCA_HASH( pNewRec->m_uiRecordID)); + + while( pRCache) + { + if( pRCache->uiDrn == pNewRec->m_uiRecordID) + { + pVersion = pRCache; + + while( pVersion) + { + if( pVersion->pRecord == pOldRec) + { + pVersion->pRecord = pNewRec; + goto Done; + } + + pVersion = pVersion->pOlderVersion; + } + } + + pRCache = pRCache->pNextInBucket; + } + +Done: + + flmAssert( pRCache); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMBOOL FlmRecordExt::canRelocateRecBuffer( + void * pvAlloc) +{ + FlmRecord * pRec = *((FlmRecord **)pvAlloc); + + flmAssert( pRec->m_pucBuffer == pvAlloc); + + if( pRec->getRefCount() == 1 && pRec->isCached()) + { + return( TRUE); + } + + return( FALSE); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void FlmRecordExt::relocateRecBuffer( + void * pvOldAlloc, + void * pvNewAlloc) +{ + FlmRecord * pRec = *((FlmRecord **)pvOldAlloc); + + flmAssert( pRec->getRefCount() == 1); + flmAssert( pRec->isCached()); + flmAssert( pRec->m_pucBuffer == pvOldAlloc); + flmAssert( pvNewAlloc < pvOldAlloc); + + // Update the buffer pointer in the record + + pRec->m_pucBuffer = (FLMBYTE *)pvNewAlloc; +} + +/**************************************************************************** +Desc: +Notes: This routine assumes the rcache mutex is locked +****************************************************************************/ +FSTATIC FLMBOOL rcaCanRelocate( + void * pvAlloc) +{ + RCACHE * pRCache = (RCACHE *)pvAlloc; + + if( RCA_IS_IN_USE( pRCache->uiFlags) || + RCA_IS_READING_IN( pRCache->uiFlags)) + { + return( FALSE); + } + + return( TRUE); +} + +/**************************************************************************** +Desc: Fixes up all pointers needed to allow an RCACHE struct to be + moved to a different location in memory +Notes: This routine assumes the rcache mutex is locked +****************************************************************************/ +FSTATIC void rcaRelocate( + void * pvOldAlloc, + void * pvNewAlloc) +{ + RCACHE * pOldRCache = (RCACHE *)pvOldAlloc; + RCACHE * pNewRCache = (RCACHE *)pvNewAlloc; + RCACHE ** ppBucket; + RCACHE_MGR * pRCacheMgr = &gv_FlmSysData.RCacheMgr; + FFILE * pFile = pOldRCache->pFile; + + flmAssert( !RCA_IS_IN_USE( pOldRCache->uiFlags)); + flmAssert( !RCA_IS_READING_IN( pOldRCache->uiFlags)); + flmAssert( !pOldRCache->pNotifyList); + + if( pNewRCache->pPrevInFile) + { + pNewRCache->pPrevInFile->pNextInFile = pNewRCache; + } + + if( pNewRCache->pNextInFile) + { + pNewRCache->pNextInFile->pPrevInFile = pNewRCache; + } + + if( pNewRCache->pPrevInGlobal) + { + pNewRCache->pPrevInGlobal->pNextInGlobal = pNewRCache; + } + + if( pNewRCache->pNextInGlobal) + { + pNewRCache->pNextInGlobal->pPrevInGlobal = pNewRCache; + } + + if( pNewRCache->pPrevInBucket) + { + pNewRCache->pPrevInBucket->pNextInBucket = pNewRCache; + } + + if( pNewRCache->pNextInBucket) + { + pNewRCache->pNextInBucket->pPrevInBucket = pNewRCache; + } + + if( pNewRCache->pOlderVersion) + { + pNewRCache->pOlderVersion->pNewerVersion = pNewRCache; + } + + if( pNewRCache->pNewerVersion) + { + pNewRCache->pNewerVersion->pOlderVersion = pNewRCache; + } + + if( pNewRCache->pPrevInHeapList) + { + pNewRCache->pPrevInHeapList->pNextInHeapList = pNewRCache; + } + + if( pNewRCache->pNextInHeapList) + { + pNewRCache->pNextInHeapList->pPrevInHeapList = pNewRCache; + } + + ppBucket = FLM_RCA_HASH( pOldRCache->uiDrn); + if( *ppBucket == pOldRCache) + { + *ppBucket = pNewRCache; + } + + if( pRCacheMgr->pHeapList == pOldRCache) + { + pRCacheMgr->pHeapList = pNewRCache; + } + + if( pRCacheMgr->pPurgeList == pOldRCache) + { + pRCacheMgr->pPurgeList = pNewRCache; + } + + if( pRCacheMgr->pMRURecord == pOldRCache) + { + pRCacheMgr->pMRURecord = pNewRCache; + } + + if( pRCacheMgr->pLRURecord == pOldRCache) + { + pRCacheMgr->pLRURecord = pNewRCache; + } + + if( pFile) + { + if( pFile->pFirstRecord == pOldRCache) + { + pFile->pFirstRecord = pNewRCache; + } + + if( pFile->pLastRecord == pOldRCache) + { + pFile->pLastRecord = pNewRCache; + } + } + +#ifdef FLM_DEBUG + f_memset( pOldRCache, 0, sizeof( RCACHE)); +#endif +}