Files
mars-flaim/flaim/src/fsysdata.cpp
2006-07-19 19:09:30 +00:00

4373 lines
103 KiB
C++

//-------------------------------------------------------------------------
// 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_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
#define DEFAULT_OPEN_THRESHOLD 100 // 100 file handles to cache
#define DEFAULT_MAX_AVAIL_TIME 900 // 15 minutes
FLMATOMIC gv_flmSysSpinLock = 0;
FLMUINT gv_uiFlmSysStartupCount = 0;
FSTATIC RCODE flmGetCacheBytes(
FLMUINT uiPercent,
FLMUINT uiMin,
FLMUINT uiMax,
FLMUINT uiMinToLeave,
FLMBOOL bCalcOnAvailMem,
FLMUINT uiBytesCurrentlyInUse,
FLMUINT * puiCacheBytes);
FSTATIC void flmLockSysData( void);
FSTATIC void flmUnlockSysData( void);
FSTATIC RCODE flmSetCacheLimits(
FLMUINT uiNewTotalCacheSize,
FLMBOOL bForceLimit,
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 FLMAPI flmSystemMonitor(
IF_Thread * pThread);
FSTATIC RCODE flmRegisterHttpCallback(
FLM_MODULE_HANDLE hModule,
const char * pszUrlString);
FSTATIC RCODE flmDeregisterHttpCallback( void);
/****************************************************************************
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->doesFileExist( 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 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.
****************************************************************************/
FSTATIC RCODE flmGetCacheBytes(
FLMUINT uiPercent,
FLMUINT uiMin,
FLMUINT uiMax,
FLMUINT uiMinToLeave,
FLMBOOL bCalcOnAvailMem,
FLMUINT uiBytesCurrentlyInUse,
FLMUINT * puiCacheBytes)
{
RCODE rc = NE_FLM_OK;
FLMUINT uiMem = 0;
FLMUINT64 ui64TotalPhysMem;
FLMUINT64 ui64AvailPhysMem;
if( RC_BAD( rc = f_getMemoryInfo( &ui64TotalPhysMem, &ui64AvailPhysMem)))
{
goto Exit;
}
if( ui64TotalPhysMem > FLM_MAX_UINT)
{
ui64TotalPhysMem = FLM_MAX_UINT;
}
if( ui64AvailPhysMem > ui64TotalPhysMem)
{
ui64AvailPhysMem = ui64TotalPhysMem;
}
uiMem = (FLMUINT)((bCalcOnAvailMem)
? (FLMUINT)ui64AvailPhysMem
: (FLMUINT)ui64TotalPhysMem);
// 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;
}
}
// 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;
}
Exit:
*puiCacheBytes = uiMem;
return( rc);
}
/***************************************************************************
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)
{
while( f_atomicExchange( &gv_flmSysSpinLock, 1) == 1)
{
f_sleep( 10);
}
}
/***************************************************************************
Desc: Unlock the system data structure for access - called only by startup
and shutdown.
***************************************************************************/
FSTATIC void flmUnlockSysData( void)
{
f_atomicExchange( &gv_flmSysSpinLock, 0);
}
/****************************************************************************
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.
****************************************************************************/
FLMEXP RCODE FLMAPI FlmStartup( void)
{
RCODE rc = FERR_OK;
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;
}
if( RC_BAD( rc = ftkStartup()))
{
goto Exit;
}
// The memset needs to be first.
f_memset( &gv_FlmSysData, 0, sizeof( FLMSYSDATA));
gv_FlmSysData.uiMaxFileSize = f_getMaxFileSize();
flmAssert( gv_FlmSysData.uiMaxFileSize);
// Initialize memory tracking variables - should be done before
// call to f_memoryInit().
gv_FlmSysData.hShareMutex = 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.
gv_FlmSysData.UpdateEvents.hMutex = F_MUTEX_NULL;
gv_FlmSysData.LockEvents.hMutex = F_MUTEX_NULL;
gv_FlmSysData.SizeEvents.hMutex = F_MUTEX_NULL;
// Set the default file open flags
gv_FlmSysData.uiFileOpenFlags =
FLM_IO_RDWR | FLM_IO_SH_DENYNONE | FLM_IO_DIRECT;
gv_FlmSysData.uiFileCreateFlags =
gv_FlmSysData.uiFileOpenFlags | FLM_IO_EXCL | FLM_IO_CREATE_DIR;
#ifdef FLM_DBG_LOG
flmDbgLogInit();
#endif
gv_FlmSysData.uiMaxUnusedTime =
FLM_SECS_TO_TIMER_UNITS( DEFAULT_MAX_UNUSED_TIME);
gv_FlmSysData.uiMaxCPInterval =
FLM_SECS_TO_TIMER_UNITS( DEFAULT_MAX_CP_INTERVAL);
gv_FlmSysData.uiMaxTransTime =
FLM_SECS_TO_TIMER_UNITS( DEFAULT_MAX_TRANS_SECS);
gv_FlmSysData.uiMaxTransInactiveTime =
FLM_SECS_TO_TIMER_UNITS( DEFAULT_MAX_TRANS_INACTIVE_SECS);
if( f_canGetMemoryInfo())
{
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;
gv_FlmSysData.uiCacheAdjustInterval =
FLM_SECS_TO_TIMER_UNITS( DEFAULT_CACHE_ADJUST_INTERVAL);
if( RC_BAD( rc = flmGetCacheBytes( gv_FlmSysData.uiCacheAdjustPercent,
gv_FlmSysData.uiCacheAdjustMin, gv_FlmSysData.uiCacheAdjustMax,
gv_FlmSysData.uiCacheAdjustMinToLeave, TRUE, 0, &uiCacheBytes)))
{
goto Exit;
}
}
else
{
gv_FlmSysData.bDynamicCacheAdjust = FALSE;
gv_FlmSysData.uiCacheAdjustInterval = 0;
uiCacheBytes = DEFAULT_CACHE_ADJUST_MIN;
}
if( uiCacheBytes > FLM_MAX_CACHE_SIZE)
{
uiCacheBytes = FLM_MAX_CACHE_SIZE;
}
gv_FlmSysData.uiBlockCachePercentage = DEFAULT_BLOCK_CACHE_PERCENTAGE;
gv_FlmSysData.uiCacheCleanupInterval =
FLM_SECS_TO_TIMER_UNITS( DEFAULT_CACHE_CLEANUP_INTERVAL);
gv_FlmSysData.uiUnusedCleanupInterval =
FLM_SECS_TO_TIMER_UNITS( DEFAULT_UNUSED_CLEANUP_INTERVAL);
// Get a pointer to the thread manager
if( RC_BAD( rc = FlmGetThreadMgr( &gv_FlmSysData.pThreadMgr)))
{
goto Exit;
}
// Allocate thread group IDs
gv_uiBackIxThrdGroup = gv_FlmSysData.pThreadMgr->allocGroupId();
gv_uiCPThrdGrp = gv_FlmSysData.pThreadMgr->allocGroupId();
gv_uiDbThrdGrp = gv_FlmSysData.pThreadMgr->allocGroupId();
// Initialize the slab manager
if( RC_BAD( rc = FlmAllocSlabManager( &gv_FlmSysData.pSlabManager)))
{
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.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 = f_allocHashTable( FILE_HASH_ENTRIES,
&gv_FlmSysData.pFileHashTbl)))
{
goto Exit;
}
gv_FlmSysData.uiNextFFileId = 1;
// Get the file system object
if( RC_BAD( rc = FlmGetFileSystem( &gv_FlmSysData.pFileSystem)))
{
goto Exit;
}
// Allocate a file handle cache
if( RC_BAD( rc = gv_FlmSysData.pFileSystem->allocFileHandleCache(
DEFAULT_OPEN_THRESHOLD, DEFAULT_MAX_UNUSED_TIME,
&gv_FlmSysData.pFileHdlCache)))
{
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 mutexes for the event table.
if (RC_BAD( rc = f_mutexCreate(
&gv_FlmSysData.UpdateEvents.hMutex)))
{
goto Exit;
}
if (RC_BAD( rc = f_mutexCreate(
&gv_FlmSysData.LockEvents.hMutex)))
{
goto Exit;
}
if (RC_BAD( rc = f_mutexCreate(
&gv_FlmSysData.SizeEvents.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,
flmSystemMonitor, "FLAIM System Monitor")))
{
goto Exit;
}
#ifdef FLM_USE_NICI
iHandle = (int)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 bForceLimit,
FLMBOOL bPreallocateCache)
{
RCODE rc = FERR_OK;
FLMUINT uiNewBlockCacheSize;
FLMBOOL bResizeAfterConfig = FALSE;
if( !bForceLimit && uiNewTotalCacheSize > FLM_MAX_CACHE_SIZE)
{
uiNewTotalCacheSize = FLM_MAX_CACHE_SIZE;
}
if( bPreallocateCache)
{
if( gv_FlmSysData.bDynamicCacheAdjust)
{
// Can't pre-allocate and dynamically adjust.
goto DONT_PREALLOCATE;
}
if( RC_BAD( rc = gv_FlmSysData.pSlabManager->resize(
uiNewTotalCacheSize, TRUE, &uiNewTotalCacheSize)))
{
// Log a message indicating that we couldn't pre-allocate
// the cache
flmLogMessage( F_DEBUG_MESSAGE, FLM_YELLOW, FLM_BLACK,
"WARNING: Couldn't pre-allocate cache.");
goto DONT_PREALLOCATE;
}
gv_FlmSysData.bCachePreallocated = TRUE;
}
else
{
DONT_PREALLOCATE:
bResizeAfterConfig = TRUE;
gv_FlmSysData.bCachePreallocated = FALSE;
}
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( uiNewTotalCacheSize, FALSE);
}
gv_FlmSysData.uiMaxCache = uiNewTotalCacheSize;
return( rc);
}
/****************************************************************************
Desc : Configures how memory will be dynamically regulated.
****************************************************************************/
FLMEXP RCODE FLMAPI FlmSetDynamicMemoryLimit(
FLMUINT uiCacheAdjustPercent,
FLMUINT uiCacheAdjustMin,
FLMUINT uiCacheAdjustMax,
FLMUINT uiCacheAdjustMinToLeave)
{
if( !f_canGetMemoryInfo())
{
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;
if( RC_OK( rc = flmGetCacheBytes( gv_FlmSysData.uiCacheAdjustPercent,
gv_FlmSysData.uiCacheAdjustMin, gv_FlmSysData.uiCacheAdjustMax,
gv_FlmSysData.uiCacheAdjustMinToLeave, TRUE,
gv_FlmSysData.SCacheMgr.Usage.uiTotalBytesAllocated +
gv_FlmSysData.SCacheMgr.Usage.uiTotalBytesAllocated,
&uiCacheBytes)))
{
rc = flmSetCacheLimits( uiCacheBytes, FALSE, FALSE);
}
f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex);
f_mutexUnlock( gv_FlmSysData.hShareMutex);
return( rc);
}
}
/****************************************************************************
Desc : Sets a hard memory limit for cache.
****************************************************************************/
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)
{
if( !f_canGetMemoryInfo())
{
rc = RC_SET( FERR_NOT_IMPLEMENTED);
}
else
{
FLMUINT uiCacheBytes;
if( RC_OK( rc = flmGetCacheBytes(
uiPercent, uiMin, uiMax, uiMinToLeave,
bPercentOfAvail, gv_FlmSysData.SCacheMgr.Usage.uiTotalBytesAllocated +
gv_FlmSysData.RCacheMgr.Usage.uiTotalBytesAllocated,
&uiCacheBytes)))
{
rc = flmSetCacheLimits( uiCacheBytes, FALSE, bPreallocate);
}
}
}
else
{
rc = flmSetCacheLimits( uiMax, TRUE, bPreallocate);
}
f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex);
f_mutexUnlock( gv_FlmSysData.hShareMutex);
return( rc);
}
/****************************************************************************
Desc : Returns information about memory usage.
****************************************************************************/
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->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 = f_notifyWait(
gv_FlmSysData.hShareMutex, F_SEM_NULL, NULL, &pFile->pOpenNotifies)))
{
// If f_notifyWait 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.
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;
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( void)
{
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);
}
/****************************************************************************
Desc: Configures share attributes.
****************************************************************************/
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:
{
rc = gv_FlmSysData.pFileHdlCache->setOpenThreshold( (FLMUINT)Value1);
break;
}
case FLM_DIRECT_IO_STATE:
{
// Direct I/O state can only be changed when there are no open
// databases. We also only allow DIO to be disabled on Unix
// platforms.
#ifdef FLM_UNIX
f_mutexLock( gv_FlmSysData.hShareMutex);
if( !gv_FlmSysData.uiOpenFFiles)
{
if( (FLMBOOL)Value1)
{
gv_FlmSysData.uiFileOpenFlags =
FLM_IO_RDWR | FLM_IO_SH_DENYNONE | FLM_IO_DIRECT;
}
else
{
gv_FlmSysData.uiFileOpenFlags =
FLM_IO_RDWR | FLM_IO_SH_DENYNONE;
}
gv_FlmSysData.uiFileCreateFlags =
gv_FlmSysData.uiFileOpenFlags | FLM_IO_EXCL | FLM_IO_CREATE_DIR;
}
f_mutexUnlock( gv_FlmSysData.hShareMutex);
#endif
break;
}
case FLM_CACHE_LIMIT:
{
f_mutexLock( gv_FlmSysData.hShareMutex);
f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex);
gv_FlmSysData.bDynamicCacheAdjust = FALSE;
rc = flmSetCacheLimits( (FLMUINT)Value1, TRUE, (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.bDynamicCacheAdjust ? FALSE : TRUE,
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
uiValue = FLM_SECS_TO_TIMER_UNITS( (FLMUINT) Value1);
// 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:
{
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:
{
gv_FlmSysData.uiMaxCPInterval =
FLM_SECS_TO_TIMER_UNITS( (FLMUINT) Value1);
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:
{
gv_FlmSysData.uiMaxTransTime =
FLM_SECS_TO_TIMER_UNITS( (FLMUINT) Value1);
break;
}
case FLM_MAX_TRANS_INACTIVE_SECS:
{
gv_FlmSysData.uiMaxTransInactiveTime =
FLM_SECS_TO_TIMER_UNITS( (FLMUINT) Value1);
break;
}
case FLM_CACHE_ADJUST_INTERVAL:
{
gv_FlmSysData.uiCacheAdjustInterval =
FLM_SECS_TO_TIMER_UNITS( (FLMUINT)Value1);
break;
}
case FLM_CACHE_CLEANUP_INTERVAL:
{
gv_FlmSysData.uiCacheCleanupInterval =
FLM_SECS_TO_TIMER_UNITS( (FLMUINT)Value1);
break;
}
case FLM_UNUSED_CLEANUP_INTERVAL:
{
gv_FlmSysData.uiUnusedCleanupInterval =
FLM_SECS_TO_TIMER_UNITS( (FLMUINT)Value1);
break;
}
case FLM_MAX_UNUSED_TIME:
{
f_mutexLock( gv_FlmSysData.hShareMutex);
gv_FlmSysData.uiMaxUnusedTime =
FLM_SECS_TO_TIMER_UNITS( (FLMUINT)Value1);
f_mutexUnlock( gv_FlmSysData.hShareMutex);
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 = (IF_LoggerClient *)Value1;
gv_FlmSysData.pLogger->AddRef();
f_setLoggerClient( gv_FlmSysData.pLogger);
}
f_mutexUnlock( gv_FlmSysData.hShareMutex);
break;
case FLM_ASSIGN_HTTP_SYMS:
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);
}
/****************************************************************************
Desc : Gets configured shared attributes.
****************************************************************************/
FLMEXP RCODE FLMAPI FlmGetConfig(
eFlmConfigTypes eConfigType,
void * Value1)
{
RCODE rc = FERR_OK;
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) = f_getOpenFileCount();
break;
}
case FLM_OPEN_THRESHOLD:
*((FLMUINT *)Value1) = gv_FlmSysData.pFileHdlCache->getOpenThreshold();
break;
case FLM_DIRECT_IO_STATE:
{
f_mutexLock( gv_FlmSysData.hShareMutex);
if( gv_FlmSysData.uiFileOpenFlags & FLM_IO_DIRECT)
{
*((FLMBOOL *)Value1) = TRUE;
}
else
{
*((FLMBOOL *)Value1) = FALSE;
}
f_mutexUnlock( gv_FlmSysData.hShareMutex);
}
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:
*((FLMUINT *)Value1) =
FLM_TIMER_UNITS_TO_SECS( gv_FlmSysData.uiMaxCPInterval);
break;
case FLM_BLOB_EXT:
f_strcpy( (char *)Value1, (const char *)gv_FlmSysData.ucBlobExt);
break;
case FLM_MAX_TRANS_SECS:
*((FLMUINT *)Value1) =
FLM_TIMER_UNITS_TO_SECS( gv_FlmSysData.uiMaxTransTime);
break;
case FLM_MAX_TRANS_INACTIVE_SECS:
*((FLMUINT *)Value1) =
FLM_TIMER_UNITS_TO_SECS( gv_FlmSysData.uiMaxTransInactiveTime);
break;
case FLM_CACHE_ADJUST_INTERVAL:
*((FLMUINT *)Value1) =
FLM_TIMER_UNITS_TO_SECS( gv_FlmSysData.uiCacheAdjustInterval);
break;
case FLM_CACHE_CLEANUP_INTERVAL:
*((FLMUINT *)Value1) =
FLM_TIMER_UNITS_TO_SECS( gv_FlmSysData.uiCacheCleanupInterval);
break;
case FLM_UNUSED_CLEANUP_INTERVAL:
*((FLMUINT *)Value1) =
FLM_TIMER_UNITS_TO_SECS( gv_FlmSysData.uiUnusedCleanupInterval);
break;
case FLM_MAX_UNUSED_TIME:
f_mutexLock( gv_FlmSysData.hShareMutex);
*((FLMUINT *)Value1) =
FLM_TIMER_UNITS_TO_SECS( gv_FlmSysData.uiMaxUnusedTime);
f_mutexUnlock( gv_FlmSysData.hShareMutex);
break;
case FLM_CACHE_CHECK:
*((FLMBOOL *)Value1) = gv_FlmSysData.bCheckCache;
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:
if( f_canGetMemoryInfo())
{
*((FLMBOOL *)Value1) = TRUE;
}
else
{
*((FLMBOOL *)Value1) = FALSE;
}
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);
}
/****************************************************************************
Desc:
****************************************************************************/
FLMEXP RCODE FLMAPI FlmGetThreadInfo(
F_Pool * pPool,
F_THREAD_INFO ** ppThreadInfo,
FLMUINT * puiNumThreads,
const char * pszUrl)
{
RCODE rc = FERR_OK;
CS_CONTEXT * 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;
IF_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, gv_uiBackIxThrdGroup, &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 database thread group
uiThreadId = 0;
for( ;;)
{
if( RC_BAD( rc = gv_FlmSysData.pThreadMgr->getNextGroupThread(
&pThread, gv_uiDbThrdGrp, &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)
{
F_NOTIFY_LIST_ITEM * 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;
}
// Shutdown the monitor thread
if( pFile->pMonitorThrd)
{
pFile->pMonitorThrd->stopThread();
pFile->pMonitorThrd->Release();
pFile->pMonitorThrd = 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;
}
// We shouldn't have any open notifies at this point
flmAssert( pFile->pOpenNotifies == NULL);
// 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);
// 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)
{
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->pucLogHdrIOBuf)
{
f_freeAlignedBuffer( (void **)&pFile->pucLogHdrIOBuf);
}
pFile->krefPool.poolFree();
if( pFile->ppBlocksDone)
{
f_free( &pFile->ppBlocksDone);
pFile->uiBlocksDoneArraySize = 0;
}
// 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;
// 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)
{
F_BUCKET * 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;
}
f_mutexUnlock( gv_FlmSysData.hShareMutex);
f_free( &gv_FlmSysData.pFileHashTbl);
}
// Free the statistics.
if (gv_FlmSysData.bStatsInitialized)
{
FlmFreeStats( &gv_FlmSysData.Stats);
gv_FlmSysData.bStatsInitialized = FALSE;
}
// 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.HttpConfigParms.hMutex != F_MUTEX_NULL)
{
f_mutexDestroy( &gv_FlmSysData.HttpConfigParms.hMutex);
}
// Free up callbacks that have been registered for events.
if (gv_FlmSysData.UpdateEvents.hMutex != F_MUTEX_NULL)
{
while (gv_FlmSysData.UpdateEvents.pEventCBList)
{
flmFreeEvent(
gv_FlmSysData.UpdateEvents.pEventCBList,
gv_FlmSysData.UpdateEvents.hMutex,
&gv_FlmSysData.UpdateEvents.pEventCBList);
}
f_mutexDestroy( &gv_FlmSysData.UpdateEvents.hMutex);
}
if (gv_FlmSysData.LockEvents.hMutex != F_MUTEX_NULL)
{
while (gv_FlmSysData.LockEvents.pEventCBList)
{
flmFreeEvent(
gv_FlmSysData.LockEvents.pEventCBList,
gv_FlmSysData.LockEvents.hMutex,
&gv_FlmSysData.LockEvents.pEventCBList);
}
f_mutexDestroy( &gv_FlmSysData.LockEvents.hMutex);
}
if (gv_FlmSysData.SizeEvents.hMutex != F_MUTEX_NULL)
{
while (gv_FlmSysData.SizeEvents.pEventCBList)
{
flmFreeEvent(
gv_FlmSysData.SizeEvents.pEventCBList,
gv_FlmSysData.SizeEvents.hMutex,
&gv_FlmSysData.SizeEvents.pEventCBList);
}
f_mutexDestroy( &gv_FlmSysData.SizeEvents.hMutex);
}
// Free (release) FLAIM's File Shared File System Object.
if( gv_FlmSysData.pFileSystem)
{
gv_FlmSysData.pFileSystem->Release();
gv_FlmSysData.pFileSystem = NULL;
}
#ifdef FLM_DBG_LOG
flmDbgLogExit();
#endif
// Release the logger (if any)
if( gv_FlmSysData.pLogger)
{
gv_FlmSysData.pLogger->Release();
gv_FlmSysData.pLogger = NULL;
}
// Release the thread manager
if( gv_FlmSysData.pThreadMgr)
{
gv_FlmSysData.pThreadMgr->Release();
gv_FlmSysData.pThreadMgr = NULL;
}
// Release the file handle cache
if( gv_FlmSysData.pFileHdlCache)
{
gv_FlmSysData.pFileHdlCache->Release();
gv_FlmSysData.pFileHdlCache = 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
// Shut down the toolkit
ftkShutdown();
}
/****************************************************************************
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.
****************************************************************************/
FLMEXP void FLMAPI FlmShutdown( void)
{
flmLockSysData();
flmCleanup();
flmUnlockSysData();
}
/****************************************************************************
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)
{
RCODE rc = FERR_OK;
FFILE * pTmpFile;
F_BUCKET * 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 = gv_FlmSysData.pFileSystem->pathToStorageString(
pFile->pszDbPath, szDbPathStr)))
{
goto Exit;
}
uiBucket = f_strHashBucket( 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;
gv_FlmSysData.uiOpenFFiles++;
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)
{
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;
}
flmAssert( gv_FlmSysData.uiOpenFFiles);
gv_FlmSysData.uiOpenFFiles--;
}
/****************************************************************************
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;
}
}
gv_FlmSysData.pFileHdlCache->closeUnusedFiles(
FLM_TIMER_UNITS_TO_SECS( gv_FlmSysData.uiMaxUnusedTime));
}
/****************************************************************************
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)
{
// 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,
FFILE * pFile)
{
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++;
}
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 FLMAPI flmSystemMonitor(
IF_Thread * pThread)
{
FLMUINT uiLastUnusedCleanupTime = 0;
FLMUINT uiLastRCacheCleanupTime = 0;
FLMUINT uiLastSCacheCleanupTime = 0;
FLMUINT uiCurrTime;
FLMUINT uiMaxLockTime;
FLMUINT uiLastCacheAdjustTime = 0;
uiMaxLockTime = FLM_MILLI_TO_TIMER_UNITS( 100);
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();
}
// Check the adjusting cache limit
if( f_canGetMemoryInfo())
{
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))
{
if( RC_OK( flmGetCacheBytes(
gv_FlmSysData.uiCacheAdjustPercent,
gv_FlmSysData.uiCacheAdjustMin,
gv_FlmSysData.uiCacheAdjustMax,
gv_FlmSysData.uiCacheAdjustMinToLeave, TRUE,
gv_FlmSysData.SCacheMgr.Usage.uiTotalBytesAllocated +
gv_FlmSysData.RCacheMgr.Usage.uiTotalBytesAllocated,
&uiCacheBytes)))
{
flmSetCacheLimits( uiCacheBytes, FALSE, FALSE);
}
}
f_mutexUnlock( gv_FlmSysData.hShareMutex);
f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex);
uiCurrTime = uiLastCacheAdjustTime = FLM_GET_TIMER();
}
}
// 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);
}
pThread->sleep( 1000);
}
return( FERR_OK);
}
/****************************************************************************
Desc : Registers a callback function to receive events.
****************************************************************************/
FLMEXP RCODE FLMAPI FlmRegisterForEvent(
FEventCategory eCategory,
FEVENT_CB fnEventCB,
void * pvAppData,
HFEVENT * phEventRV)
{
RCODE rc = FERR_OK;
FEVENT * pEvent;
*phEventRV = HFEVENT_NULL;
// 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;
// Mutex should be locked to link into list.
switch( eCategory)
{
case F_EVENT_UPDATES:
{
f_mutexLock( gv_FlmSysData.UpdateEvents.hMutex);
if ((pEvent->pNext =
gv_FlmSysData.UpdateEvents.pEventCBList) != NULL)
{
pEvent->pNext->pPrev = pEvent;
}
gv_FlmSysData.UpdateEvents.pEventCBList = pEvent;
f_mutexUnlock( gv_FlmSysData.UpdateEvents.hMutex);
break;
}
case F_EVENT_LOCKS:
{
f_mutexLock( gv_FlmSysData.LockEvents.hMutex);
if ((pEvent->pNext =
gv_FlmSysData.LockEvents.pEventCBList) != NULL)
{
pEvent->pNext->pPrev = pEvent;
}
gv_FlmSysData.LockEvents.pEventCBList = pEvent;
f_mutexUnlock( gv_FlmSysData.LockEvents.hMutex);
break;
}
case F_EVENT_SIZE:
{
f_mutexLock( gv_FlmSysData.SizeEvents.hMutex);
if ((pEvent->pNext =
gv_FlmSysData.SizeEvents.pEventCBList) != NULL)
{
pEvent->pNext->pPrev = pEvent;
}
gv_FlmSysData.SizeEvents.pEventCBList = pEvent;
f_mutexUnlock( gv_FlmSysData.SizeEvents.hMutex);
break;
}
default:
{
rc = RC_SET_AND_ASSERT( FERR_NOT_IMPLEMENTED);
goto Exit;
}
}
Exit:
return( rc);
}
/****************************************************************************
Desc : Deregisters a callback function that was registered to receive events.
****************************************************************************/
FLMEXP void FLMAPI FlmDeregisterForEvent(
HFEVENT * phEventRV)
{
if (phEventRV && *phEventRV != HFEVENT_NULL)
{
FEVENT * pEvent = (FEVENT *)(*phEventRV);
switch( pEvent->eCategory)
{
case F_EVENT_UPDATES:
{
flmFreeEvent( pEvent,
gv_FlmSysData.UpdateEvents.hMutex,
&gv_FlmSysData.UpdateEvents.pEventCBList);
break;
}
case F_EVENT_LOCKS:
{
flmFreeEvent( pEvent,
gv_FlmSysData.LockEvents.hMutex,
&gv_FlmSysData.LockEvents.pEventCBList);
break;
}
case F_EVENT_SIZE:
{
flmFreeEvent( pEvent,
gv_FlmSysData.SizeEvents.hMutex,
&gv_FlmSysData.SizeEvents.pEventCBList);
break;
}
default:
{
flmAssert( 0);
}
}
*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;
switch( eCategory)
{
case F_EVENT_UPDATES:
{
f_mutexLock( gv_FlmSysData.UpdateEvents.hMutex);
pEvent = gv_FlmSysData.UpdateEvents.pEventCBList;
while (pEvent)
{
(*pEvent->fnEventCB)( eEventType, pEvent->pvAppData,
pvEventData1,
pvEventData2);
pEvent = pEvent->pNext;
}
f_mutexUnlock( gv_FlmSysData.UpdateEvents.hMutex);
break;
}
case F_EVENT_LOCKS:
{
f_mutexLock( gv_FlmSysData.LockEvents.hMutex);
pEvent = gv_FlmSysData.LockEvents.pEventCBList;
while (pEvent)
{
(*pEvent->fnEventCB)( eEventType, pEvent->pvAppData,
pvEventData1,
pvEventData2);
pEvent = pEvent->pNext;
}
f_mutexUnlock( gv_FlmSysData.LockEvents.hMutex);
break;
}
case F_EVENT_SIZE:
{
f_mutexLock( gv_FlmSysData.SizeEvents.hMutex);
pEvent = gv_FlmSysData.SizeEvents.pEventCBList;
while (pEvent)
{
(*pEvent->fnEventCB)( eEventType, pEvent->pvAppData,
pvEventData1,
pvEventData2);
pEvent = pEvent->pNext;
}
f_mutexUnlock( gv_FlmSysData.SizeEvents.hMutex);
break;
}
default:
{
flmAssert( 0);
}
}
}
/****************************************************************************
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_pDbTable = NULL;
f_memset( m_ucKey, 0, sizeof( m_ucKey));
}
/****************************************************************************
Desc: Destructor
****************************************************************************/
F_Session::~F_Session()
{
flmAssert( !m_pPrev);
flmAssert( !m_pNext);
flmAssert( !getRefCount());
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();
}
}
/****************************************************************************
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_pDbTable = f_new F_HashTable) == NULL)
{
rc = RC_SET( FERR_MEM);
goto Exit;
}
if( RC_BAD( rc = m_pDbTable->setupHashTable( FALSE, 16, 0)))
{
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;
const 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 = pSessionDb->getKeyLength();
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->getObjectType() == 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( getRefCount());
f_mutexLock( m_hMutex);
if( m_uiThreadId && m_uiThreadId != f_threadId())
{
if( !bWait)
{
rc = RC_SET( FERR_TIMEOUT);
goto Exit;
}
if( RC_BAD( rc = f_notifyWait( m_hMutex, F_SEM_NULL, NULL,
&m_pNotifyList)))
{
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( void)
{
F_SEM hSem;
flmAssert( getRefCount());
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
****************************************************************************/
const void * FLMAPI F_Session::getKey( void)
{
return( m_ucKey);
}
/****************************************************************************
Desc:
****************************************************************************/
FLMUINT FLMAPI F_Session::getKeyLength( void)
{
return( sizeof( m_ucKey));
}
/****************************************************************************
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( getRefCount());
iRefCnt = ++m_refCnt;
f_mutexUnlock( m_hMutex);
return( iRefCnt);
}
/****************************************************************************
Desc: Decrements the objects use count
****************************************************************************/
FLMINT F_Session::Release( void)
{
FLMINT iRefCnt;
flmAssert( getRefCount());
f_mutexLock( m_hMutex);
if( (iRefCnt = --m_refCnt) == 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);
}
}
/****************************************************************************
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->getObjectType() == 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->getObjectType() == 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;
}
// 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, 0)))
{
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->getObjectType() == 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);
uiElapSecs = FLM_TIMER_UNITS_TO_SECS( uiElapTime);
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_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
****************************************************************************/
const void * FLMAPI F_SessionDb::getKey( void)
{
return( m_ucKey);
}
/****************************************************************************
Desc: Returns the key and key length of a database object
****************************************************************************/
FLMUINT FLMAPI F_SessionDb::getKeyLength( void)
{
return( sizeof( m_ucKey));
}
/****************************************************************************
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:
****************************************************************************/
F_SuperFileClient::F_SuperFileClient()
{
m_pszCFileName = NULL;
m_pszDataFileBaseName = NULL;
m_uiExtOffset = 0;
m_uiDataExtOffset = 0;
m_uiDbVersion = 0;
}
/****************************************************************************
Desc:
****************************************************************************/
F_SuperFileClient::~F_SuperFileClient()
{
if( m_pszCFileName)
{
f_free( &m_pszCFileName);
}
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE F_SuperFileClient::setup(
const char * pszCFileName,
const char * pszDataDir,
FLMUINT uiDbVersion)
{
RCODE rc = NE_FLM_OK;
FLMUINT uiNameLen;
FLMUINT uiDataNameLen;
char szDir[ F_PATH_MAX_SIZE];
char szBaseName[ F_FILENAME_SIZE];
if( !pszCFileName && *pszCFileName == 0)
{
rc = RC_SET( NE_FLM_IO_INVALID_FILENAME);
goto Exit;
}
uiNameLen = f_strlen( pszCFileName);
if (pszDataDir && *pszDataDir)
{
if (RC_BAD( rc = gv_FlmSysData.pFileSystem->pathReduce(
pszCFileName, szDir, szBaseName)))
{
goto Exit;
}
f_strcpy( szDir, pszDataDir);
if (RC_BAD( rc = gv_FlmSysData.pFileSystem->pathAppend(
szDir, szBaseName)))
{
goto Exit;
}
uiDataNameLen = f_strlen( szDir);
if (RC_BAD( rc = f_alloc( (uiNameLen + 1) + (uiDataNameLen + 1),
&m_pszCFileName)))
{
goto Exit;
}
f_memcpy( m_pszCFileName, pszCFileName, uiNameLen + 1);
m_pszDataFileBaseName = m_pszCFileName + uiNameLen + 1;
flmGetDbBasePath( m_pszDataFileBaseName, szDir, &m_uiDataExtOffset);
m_uiExtOffset = uiNameLen - (uiDataNameLen - m_uiDataExtOffset);
}
else
{
if (RC_BAD( rc = f_alloc( (uiNameLen + 1) * 2, &m_pszCFileName)))
{
goto Exit;
}
f_memcpy( m_pszCFileName, pszCFileName, uiNameLen + 1);
m_pszDataFileBaseName = m_pszCFileName + uiNameLen + 1;
flmGetDbBasePath( m_pszDataFileBaseName,
m_pszCFileName, &m_uiDataExtOffset);
m_uiExtOffset = m_uiDataExtOffset;
}
m_uiDbVersion = uiDbVersion;
Exit:
return( rc);
}
/****************************************************************************
Desc:
****************************************************************************/
FLMUINT FLMAPI F_SuperFileClient::getFileNumber(
FLMUINT uiBlockAddr)
{
return( FSGetFileNumber( uiBlockAddr));
}
/****************************************************************************
Desc:
****************************************************************************/
FLMUINT FLMAPI F_SuperFileClient::getFileOffset(
FLMUINT uiBlockAddr)
{
return( FSGetFileOffset( uiBlockAddr));
}
/****************************************************************************
Desc:
****************************************************************************/
FLMUINT FLMAPI F_SuperFileClient::getBlockAddress(
FLMUINT uiFileNumber,
FLMUINT uiFileOffset)
{
return( FSBlkAddress( uiFileNumber, uiFileOffset));
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE FLMAPI F_SuperFileClient::getFilePath(
FLMUINT uiFileNumber,
char * pszPath)
{
RCODE rc = NE_FLM_OK;
FLMUINT uiExtOffset;
if (!uiFileNumber)
{
f_strcpy( pszPath, m_pszCFileName);
goto Exit;
}
if ((m_uiDbVersion >= FLM_FILE_FORMAT_VER_4_3 &&
uiFileNumber <= MAX_DATA_FILE_NUM_VER43) ||
uiFileNumber <= MAX_DATA_FILE_NUM_VER40)
{
f_memcpy( pszPath, m_pszDataFileBaseName, m_uiDataExtOffset);
uiExtOffset = m_uiDataExtOffset;
}
else
{
f_memcpy( pszPath, m_pszCFileName, m_uiExtOffset);
uiExtOffset = m_uiExtOffset;
}
// Modify the file's extension.
bldSuperFileExtension( m_uiDbVersion,
uiFileNumber, &pszPath[ uiExtOffset]);
Exit:
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 F_SuperFileClient::bldSuperFileExtension(
FLMUINT uiDbVersion,
FLMUINT uiFileNum,
char * pszFileExtension)
{
FLMBYTE ucLetter;
flmAssert( uiDbVersion);
if (uiDbVersion >= FLM_FILE_FORMAT_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
{
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++ = f_getBase24DigitChar( (FLMBYTE)((uiFileNum & 511) / 24));
*pszFileExtension++ = f_getBase24DigitChar( (FLMBYTE)((uiFileNum & 511) % 24));
*pszFileExtension++ = ucLetter;
*pszFileExtension = 0;
}