git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@7 0109f412-320b-0410-ab79-c3e0c5ffbbe6
5244 lines
122 KiB
C++
5244 lines
122 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_AIX
|
|
#include <sys/vminfo.h>
|
|
#endif
|
|
#ifdef FLM_HPUX
|
|
#include <sys/pstat.h>
|
|
#include <sys/param.h>
|
|
#include <sys/unistd.h>
|
|
#endif
|
|
|
|
#if defined( FLM_WIN) || defined( FLM_NLM)
|
|
#define FLM_CAN_GET_PHYS_MEM
|
|
#elif defined( FLM_UNIX)
|
|
#if defined( _SC_AVPHYS_PAGES) || defined( FLM_HPUX)
|
|
#define FLM_CAN_GET_PHYS_MEM
|
|
#endif
|
|
#endif
|
|
|
|
#define FLM_MIN_FREE_BYTES (2 * 1024 * 1024)
|
|
|
|
#ifdef FLM_32BIT
|
|
#if defined( FLM_LINUX)
|
|
|
|
// With mmap'd memory on Linux, you're effectively limited to about ~2 GB.
|
|
// Userspace only gets ~3GB of useable address space anyway, and then you
|
|
// have all of the thread stacks too, which you can't have
|
|
// overlapping the heap.
|
|
|
|
#define FLM_MAX_CACHE_SIZE (1500 * 1024 * 1024)
|
|
#else
|
|
#define FLM_MAX_CACHE_SIZE (2000 * 1024 * 1024)
|
|
#endif
|
|
#else
|
|
#define FLM_MAX_CACHE_SIZE (~((FLMUINT)0))
|
|
#endif
|
|
|
|
|
|
FLMUINT32 gv_ui32FlmSysSpinLock = 0;
|
|
FLMUINT gv_uiFlmSysStartupCount = 0;
|
|
FLMBOOL gv_bNetWareStartupCalled = FALSE;
|
|
|
|
#ifdef FLM_NLM
|
|
extern "C"
|
|
{
|
|
void flmHttpConfig(
|
|
FLMBOOL bEnable,
|
|
const char * pszParams);
|
|
}
|
|
#endif
|
|
|
|
FSTATIC void flmInitHashTbl(
|
|
FBUCKET * pHashTable,
|
|
FLMUINT uiHashEntries,
|
|
f_randomGenerator * RandGen);
|
|
|
|
#ifdef FLM_CAN_GET_PHYS_MEM
|
|
FSTATIC FLMUINT flmGetCacheBytes(
|
|
FLMUINT uiPercent,
|
|
FLMUINT uiMin,
|
|
FLMUINT uiMax,
|
|
FLMUINT uiMinToLeave,
|
|
FLMBOOL bCalcOnAvailMem,
|
|
FLMUINT uiBytesCurrentlyInUse);
|
|
#endif
|
|
|
|
FSTATIC void flmLockSysData( void);
|
|
|
|
FSTATIC void flmUnlockSysData( void);
|
|
|
|
FSTATIC RCODE flmSetCacheLimits(
|
|
FLMUINT uiNewTotalCacheSize,
|
|
FLMBOOL bPreallocateCache);
|
|
|
|
FSTATIC void flmFreeEvent(
|
|
FEVENT * pEvent,
|
|
F_MUTEX hMutex,
|
|
FEVENT ** ppEventListRV);
|
|
|
|
FSTATIC RCODE flmCloseDbFile(
|
|
const char * pszDbFileName,
|
|
const char * pszDataDir);
|
|
|
|
FSTATIC void flmShutdownDbThreads(
|
|
FFILE * pFile);
|
|
|
|
FSTATIC void flmCleanup( void);
|
|
|
|
FSTATIC void flmUnlinkFileFromBucket(
|
|
FFILE * pFile);
|
|
|
|
RCODE flmMonitor(
|
|
F_Thread * pThread);
|
|
|
|
FSTATIC RCODE flmRegisterHttpCallback(
|
|
FLM_MODULE_HANDLE hModule,
|
|
const char * pszUrlString);
|
|
|
|
FSTATIC RCODE flmDeregisterHttpCallback( void);
|
|
|
|
/****************************************************************************
|
|
Desc: This routine frees all of the notify requests in a list of requests.
|
|
****************************************************************************/
|
|
FINLINE void flmFreeNotifyList(
|
|
FNOTIFY ** ppNotifyRV)
|
|
{
|
|
FNOTIFY * pTmp;
|
|
FNOTIFY * pNotify = *ppNotifyRV;
|
|
|
|
while (pNotify)
|
|
{
|
|
f_semDestroy( &pNotify->hSem);
|
|
pTmp = pNotify;
|
|
pNotify = pNotify->pNext;
|
|
(void)f_free( &pTmp);
|
|
}
|
|
|
|
*ppNotifyRV = NULL;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Sets the path for all temporary files that come into use within a
|
|
FLAIM share structure. The share mutex should be locked when
|
|
settting when called from FlmConfig().
|
|
****************************************************************************/
|
|
FINLINE RCODE flmSetTmpDir(
|
|
const char * pszTmpDir)
|
|
{
|
|
RCODE rc;
|
|
|
|
if( RC_BAD( rc = gv_FlmSysData.pFileSystem->Exists( pszTmpDir)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
f_strcpy( gv_FlmSysData.szTempDir, pszTmpDir);
|
|
gv_FlmSysData.bTempDirSet = TRUE;
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine frees all of the local dictionaries in a list of
|
|
local dictionaries.
|
|
****************************************************************************/
|
|
FINLINE void flmFreeDictList(
|
|
FDICT ** ppDictRV)
|
|
{
|
|
FDICT * pTmp;
|
|
FDICT * pDict = *ppDictRV;
|
|
|
|
while (pDict)
|
|
{
|
|
pTmp = pDict;
|
|
pDict = pDict->pNext;
|
|
flmFreeDict( pTmp);
|
|
}
|
|
|
|
*ppDictRV = NULL;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine initializes a hash table.
|
|
****************************************************************************/
|
|
FSTATIC void flmInitHashTbl(
|
|
FBUCKET * pHashTable,
|
|
FLMUINT uiHashEntries,
|
|
f_randomGenerator * RandGen)
|
|
{
|
|
FLMUINT uiCnt;
|
|
FLMUINT uiRandVal;
|
|
FLMUINT uiTempVal;
|
|
|
|
for( uiCnt = 0; uiCnt < uiHashEntries; uiCnt++)
|
|
{
|
|
pHashTable [uiCnt].uiHashValue = (FLMBYTE)uiCnt;
|
|
pHashTable [uiCnt].pFirstInBucket = NULL;
|
|
}
|
|
|
|
if (uiHashEntries <= 256)
|
|
{
|
|
for (uiCnt = 0; uiCnt < uiHashEntries - 1; uiCnt++)
|
|
{
|
|
uiRandVal = (FLMBYTE) f_randomChoice( RandGen, (FLMINT32)uiCnt,
|
|
(FLMINT32)(uiHashEntries - 1));
|
|
if (uiRandVal != uiCnt)
|
|
{
|
|
uiTempVal = (FLMBYTE)pHashTable [uiCnt].uiHashValue;
|
|
pHashTable [uiCnt].uiHashValue = pHashTable [uiRandVal].uiHashValue;
|
|
pHashTable [uiRandVal].uiHashValue = uiTempVal;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine allocates and initializes a hash table.
|
|
****************************************************************************/
|
|
RCODE flmAllocHashTbl(
|
|
FLMUINT uiHashTblSize,
|
|
FBUCKET ** ppHashTblRV)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
f_randomGenerator RandGen;
|
|
FBUCKET * pHashTbl = NULL;
|
|
|
|
// Allocate memory for the hash table
|
|
|
|
if( RC_BAD( rc = f_calloc(
|
|
(FLMUINT)(sizeof( FBUCKET)) * uiHashTblSize, &pHashTbl)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Initialize the hash table
|
|
|
|
f_randomSetSeed( &RandGen, 1);
|
|
flmInitHashTbl( pHashTbl, uiHashTblSize, &RandGen);
|
|
|
|
Exit:
|
|
|
|
*ppHashTblRV = pHashTbl;
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine determines the number of cache bytes to use for caching
|
|
based on a percentage of available physical memory or a percentage
|
|
of physical memory (depending on bCalcOnAvailMem flag).
|
|
uiBytesCurrentlyInUse indicates how many bytes are currently allocated
|
|
by FLAIM - so it can factor that in if the calculation is to be based
|
|
on the available memory.
|
|
Lower limit is 1 megabyte.
|
|
****************************************************************************/
|
|
#ifdef FLM_CAN_GET_PHYS_MEM
|
|
FSTATIC FLMUINT flmGetCacheBytes(
|
|
FLMUINT uiPercent,
|
|
FLMUINT uiMin,
|
|
FLMUINT uiMax,
|
|
FLMUINT uiMinToLeave,
|
|
FLMBOOL bCalcOnAvailMem,
|
|
FLMUINT uiBytesCurrentlyInUse
|
|
)
|
|
{
|
|
FLMUINT uiMem;
|
|
#if defined( FLM_WIN)
|
|
MEMORYSTATUS MemStatus;
|
|
#elif defined( FLM_UNIX)
|
|
FLMUINT uiProcMemLimit = FLM_MAX_UINT;
|
|
FLMUINT uiProcVMemLimit = FLM_MAX_UINT;
|
|
#endif
|
|
|
|
#if defined( FLM_WIN)
|
|
GlobalMemoryStatus( &MemStatus);
|
|
uiMem = (FLMUINT)((bCalcOnAvailMem)
|
|
? (FLMUINT)MemStatus.dwAvailPhys
|
|
: (FLMUINT)MemStatus.dwTotalPhys);
|
|
#elif defined( FLM_UNIX)
|
|
{
|
|
#ifdef FLM_AIX
|
|
struct vminfo tmpvminfo;
|
|
#ifdef _SC_PAGESIZE
|
|
long iPageSize = sysconf(_SC_PAGESIZE);
|
|
#else
|
|
long iPageSize = 4096;
|
|
#endif
|
|
|
|
if( iPageSize == -1)
|
|
{
|
|
// If sysconf returned an error, resort to using the default
|
|
// page size for the Power architecture.
|
|
|
|
iPageSize = 4096;
|
|
}
|
|
|
|
uiMem = FLM_MAX_UINT;
|
|
if( vmgetinfo( &tmpvminfo, VMINFO, sizeof( tmpvminfo)) != -1)
|
|
{
|
|
if( bCalcOnAvailMem)
|
|
{
|
|
if( tmpvminfo.numfrb < FLM_MAX_UINT)
|
|
{
|
|
uiMem = (FLMUINT)tmpvminfo.numfrb;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( tmpvminfo.memsizepgs < FLM_MAX_UINT)
|
|
{
|
|
uiMem = (FLMUINT)tmpvminfo.memsizepgs;
|
|
}
|
|
}
|
|
}
|
|
#elif defined( FLM_HPUX)
|
|
long iPageSize;
|
|
struct pst_static pst;
|
|
|
|
if (pstat_getstatic( &pst, sizeof( pst), (size_t)1, 0) == -1)
|
|
{
|
|
iPageSize = 4096;
|
|
}
|
|
else
|
|
{
|
|
iPageSize = pst.page_size;
|
|
}
|
|
if (bCalcOnAvailMem)
|
|
{
|
|
struct pst_dynamic dyn;
|
|
|
|
if (pstat_getdynamic( &dyn, sizeof( dyn), 1, 0) != -1)
|
|
{
|
|
uiMem = (FLMUINT)dyn.psd_free;
|
|
}
|
|
else
|
|
{
|
|
uiMem = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
uiMem = (FLMUINT)pst.physical_memory;
|
|
}
|
|
#else
|
|
|
|
long iPageSize = sysconf(_SC_PAGESIZE);
|
|
|
|
// Get the amount of memory available to the system
|
|
|
|
uiMem = (FLMUINT)((bCalcOnAvailMem)
|
|
? (FLMUINT)sysconf(_SC_AVPHYS_PAGES)
|
|
: (FLMUINT)sysconf(_SC_PHYS_PAGES));
|
|
#endif
|
|
|
|
if (FLM_MAX_UINT / (FLMUINT)iPageSize >= uiMem)
|
|
{
|
|
uiMem *= (FLMUINT)iPageSize;
|
|
}
|
|
else
|
|
{
|
|
uiMem = FLM_MAX_UINT;
|
|
}
|
|
|
|
#if defined( RLIMIT_VMEM)
|
|
// Bump the process soft virtual limit up to the hard limit
|
|
{
|
|
struct rlimit rlim;
|
|
|
|
if( getrlimit( RLIMIT_VMEM, &rlim) == 0)
|
|
{
|
|
if( rlim.rlim_cur < rlim.rlim_max)
|
|
{
|
|
rlim.rlim_cur = rlim.rlim_max;
|
|
(void)setrlimit( RLIMIT_VMEM, &rlim);
|
|
if( getrlimit( RLIMIT_VMEM, &rlim) != 0)
|
|
{
|
|
rlim.rlim_cur = RLIM_INFINITY;
|
|
rlim.rlim_max = RLIM_INFINITY;
|
|
}
|
|
}
|
|
|
|
if( rlim.rlim_cur != RLIM_INFINITY)
|
|
{
|
|
uiProcVMemLimit = (FLMUINT)rlim.rlim_cur;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if defined( RLIMIT_DATA)
|
|
|
|
// Bump the process soft heap limit up to the hard limit
|
|
{
|
|
struct rlimit rlim;
|
|
|
|
if( getrlimit( RLIMIT_DATA, &rlim) == 0)
|
|
{
|
|
if( rlim.rlim_cur < rlim.rlim_max)
|
|
{
|
|
rlim.rlim_cur = rlim.rlim_max;
|
|
(void)setrlimit( RLIMIT_DATA, &rlim);
|
|
if( getrlimit( RLIMIT_DATA, &rlim) != 0)
|
|
{
|
|
rlim.rlim_cur = RLIM_INFINITY;
|
|
rlim.rlim_max = RLIM_INFINITY;
|
|
}
|
|
}
|
|
|
|
if( rlim.rlim_cur != RLIM_INFINITY)
|
|
{
|
|
uiProcMemLimit = (FLMUINT)rlim.rlim_cur;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
}
|
|
#elif defined( FLM_NLM)
|
|
{
|
|
FLMUINT uiCacheBufferSize = GetCacheBufferSize();
|
|
|
|
uiMem = (FLMUINT)((bCalcOnAvailMem)
|
|
? (FLMUINT)GetCurrentNumberOfCacheBuffers()
|
|
: (FLMUINT)GetOriginalNumberOfCacheBuffers());
|
|
|
|
// Operating System will never give up last three hundred buffers.
|
|
|
|
if (uiMem > 300)
|
|
{
|
|
uiMem -= 300;
|
|
}
|
|
else
|
|
{
|
|
uiMem = 0;
|
|
}
|
|
if (uiMem > FLM_MAX_UINT / uiCacheBufferSize)
|
|
{
|
|
uiMem = FLM_MAX_UINT;
|
|
}
|
|
else
|
|
{
|
|
uiMem *= uiCacheBufferSize;
|
|
}
|
|
|
|
// Get available memory in local process pool
|
|
|
|
if (bCalcOnAvailMem)
|
|
{
|
|
FLMUINT uiFreeBytes;
|
|
FLMUINT uiFreeNodes;
|
|
FLMUINT uiAllocatedBytes;
|
|
FLMUINT uiAllocatedNodes;
|
|
FLMUINT uiTotalMemory;
|
|
|
|
if (GetNLMAllocMemoryCounts( f_getNLMHandle(),
|
|
&uiFreeBytes, &uiFreeNodes,
|
|
&uiAllocatedBytes, &uiAllocatedNodes,
|
|
&uiTotalMemory) == 0)
|
|
{
|
|
if (uiMem > FLM_MAX_UINT - uiFreeBytes)
|
|
{
|
|
uiMem = FLM_MAX_UINT;
|
|
}
|
|
else
|
|
{
|
|
uiMem += uiFreeBytes;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
#error Getting physical memory is not supported by this platform.
|
|
#endif
|
|
|
|
// If we are basing the calculation on available physical memory,
|
|
// take in to account what has already been allocated.
|
|
|
|
if (bCalcOnAvailMem)
|
|
{
|
|
if (uiMem > FLM_MAX_UINT - uiBytesCurrentlyInUse)
|
|
{
|
|
uiMem = FLM_MAX_UINT;
|
|
}
|
|
else
|
|
{
|
|
uiMem += uiBytesCurrentlyInUse;
|
|
}
|
|
}
|
|
|
|
// Determine if there are limits on the amount of memory the
|
|
// process can access and reset uiMem accordingly. There may
|
|
// be more available memory than the process is able to access.
|
|
|
|
#ifdef FLM_WIN
|
|
|
|
// There could be more physical memory in the system than we could
|
|
// actually allocate in our virtual address space. Thus, we need to
|
|
// make sure that we never exceed our total virtual address space.
|
|
|
|
if (uiMem > (FLMUINT)MemStatus.dwTotalVirtual)
|
|
{
|
|
uiMem = (FLMUINT)MemStatus.dwTotalVirtual;
|
|
}
|
|
|
|
#elif defined( FLM_UNIX)
|
|
|
|
// The process might be limited in the amount of memory it
|
|
// can access.
|
|
|
|
if ( uiMem > uiProcMemLimit)
|
|
{
|
|
uiMem = uiProcMemLimit;
|
|
}
|
|
|
|
if( uiMem > uiProcVMemLimit)
|
|
{
|
|
uiMem = uiProcVMemLimit;
|
|
}
|
|
|
|
#endif
|
|
|
|
// If uiMax is zero, use uiMinToLeave to calculate the maximum.
|
|
|
|
if (!uiMax)
|
|
{
|
|
if (!uiMinToLeave)
|
|
{
|
|
uiMax = uiMem;
|
|
}
|
|
else if (uiMinToLeave < uiMem)
|
|
{
|
|
uiMax = uiMem - uiMinToLeave;
|
|
}
|
|
else
|
|
{
|
|
uiMax = 0;
|
|
}
|
|
}
|
|
|
|
// Calculate memory as a percentage of memory.
|
|
|
|
uiMem = (FLMUINT)((uiMem > FLM_MAX_UINT / 100)
|
|
? (FLMUINT)(uiMem / 100) * uiPercent
|
|
: (FLMUINT)(uiMem * uiPercent) / 100);
|
|
|
|
// Don't go above the maximum.
|
|
|
|
if (uiMem > uiMax)
|
|
{
|
|
uiMem = uiMax;
|
|
}
|
|
|
|
// Don't go below the minimum.
|
|
|
|
if (uiMem < uiMin)
|
|
{
|
|
uiMem = uiMin;
|
|
}
|
|
return( uiMem);
|
|
}
|
|
#endif
|
|
|
|
/***************************************************************************
|
|
Desc: Lock the system data structure for access - called only by startup
|
|
and shutdown. NOTE: On platforms that do not support atomic exchange
|
|
this is less than perfect - won't handle tight race conditions.
|
|
***************************************************************************/
|
|
FSTATIC void flmLockSysData( void)
|
|
{
|
|
#ifdef ATOMIC_INCDEC_SUPPORT
|
|
|
|
// Obtain the spin lock
|
|
|
|
while (ftkAtomicExchange( &gv_ui32FlmSysSpinLock, 1) == 1)
|
|
{
|
|
f_sleep( 10);
|
|
}
|
|
#else
|
|
while (gv_ui32FlmSysSpinLock)
|
|
{
|
|
f_sleep( 10);
|
|
}
|
|
gv_ui32FlmSysSpinLock = 1;
|
|
#endif
|
|
}
|
|
|
|
/***************************************************************************
|
|
Desc: Unlock the system data structure for access - called only by startup
|
|
and shutdown.
|
|
***************************************************************************/
|
|
FSTATIC void flmUnlockSysData( void)
|
|
{
|
|
#ifdef ATOMIC_INCDEC_SUPPORT
|
|
(void)ftkAtomicExchange( &gv_ui32FlmSysSpinLock, 0);
|
|
#else
|
|
gv_ui32FlmSysSpinLock = 0;
|
|
#endif
|
|
}
|
|
|
|
/*API~***********************************************************************
|
|
Desc : Startup FLAIM.
|
|
Notes: This routine may be called multiple times. However, if that is done
|
|
FlmShutdown() should be called for each time this is called
|
|
successfully. This routine does not handle race conditions on
|
|
platforms that do not support atomic increment.
|
|
*END************************************************************************/
|
|
RCODE FlmStartup( void)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FLMINT iEventCategory;
|
|
FLMUINT uiCacheBytes;
|
|
#ifdef FLM_USE_NICI
|
|
int iHandle;
|
|
#endif
|
|
|
|
flmLockSysData();
|
|
|
|
// See if FLAIM has already been started. If so,
|
|
// we are done.
|
|
|
|
if (++gv_uiFlmSysStartupCount > 1)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
#ifdef FLM_NLM
|
|
gv_bNetWareStartupCalled = TRUE;
|
|
if( RC_BAD( rc = f_netwareStartup()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
#endif
|
|
|
|
// Sanity check -- make sure we are using the correct
|
|
// byte-swap macros for this platform
|
|
|
|
flmAssert( FB2UD( "\x0A\x0B\x0C\x0D") == 0x0D0C0B0A);
|
|
flmAssert( FB2UW( "\x0A\x0B") == 0x0B0A);
|
|
|
|
// The memset needs to be first.
|
|
|
|
f_memset( &gv_FlmSysData, 0, sizeof( FLMSYSDATA));
|
|
|
|
#if defined( FLM_LINUX)
|
|
flmGetLinuxKernelVersion( &gv_FlmSysData.uiLinuxMajorVer,
|
|
&gv_FlmSysData.uiLinuxMinorVer,
|
|
&gv_FlmSysData.uiLinuxRevision);
|
|
gv_FlmSysData.uiMaxFileSize = flmGetLinuxMaxFileSize( sizeof( FLMUINT));
|
|
#elif defined( FLM_AIX)
|
|
// Call set setrlimit to increase the max allowed file size.
|
|
// We don't have a good way to deal with any errors returned by
|
|
// setrlimit(), so we just hope that there aren't any...
|
|
struct rlimit rlim;
|
|
rlim.rlim_cur = RLIM_INFINITY;
|
|
rlim.rlim_max = RLIM_INFINITY;
|
|
setrlimit( RLIMIT_FSIZE, &rlim);
|
|
gv_FlmSysData.uiMaxFileSize = F_MAXIMUM_FILE_SIZE;
|
|
#else
|
|
gv_FlmSysData.uiMaxFileSize = F_MAXIMUM_FILE_SIZE;
|
|
#endif
|
|
|
|
flmAssert( gv_FlmSysData.uiMaxFileSize);
|
|
|
|
// Initialize memory tracking variables - should be done before
|
|
// call to f_memoryInit().
|
|
|
|
#ifdef FLM_DEBUG
|
|
|
|
// Variables for memory allocation tracking.
|
|
|
|
gv_FlmSysData.bTrackLeaks = TRUE;
|
|
gv_FlmSysData.hMemTrackingMutex = F_MUTEX_NULL;
|
|
#ifdef DEBUG_SIM_OUT_OF_MEM
|
|
f_randomSetSeed( &gv_FlmSysData.memSimRandomGen, 1);
|
|
#endif
|
|
#endif
|
|
|
|
gv_FlmSysData.hShareMutex = F_MUTEX_NULL;
|
|
gv_FlmSysData.hFileHdlMutex = F_MUTEX_NULL;
|
|
gv_FlmSysData.uiMaxStratifyIterations = DEFAULT_MAX_STRATIFY_ITERATIONS;
|
|
gv_FlmSysData.uiMaxStratifyTime = DEFAULT_MAX_STRATIFY_TIME;
|
|
|
|
// Initialize the event categories to have no mutex.
|
|
|
|
for (iEventCategory = 0;
|
|
iEventCategory < F_MAX_EVENT_CATEGORIES;
|
|
iEventCategory++)
|
|
{
|
|
gv_FlmSysData.EventHdrs [iEventCategory].hMutex = F_MUTEX_NULL;
|
|
}
|
|
|
|
// Memory initialization should be first.
|
|
|
|
f_memoryInit();
|
|
|
|
#if defined( FLM_NLM) || (defined( FLM_WIN) && !defined( FLM_64BIT))
|
|
/* Initialize the checksum code variable. */
|
|
InitFastBlockCheckSum();
|
|
#endif
|
|
|
|
#if defined( FLM_NLM)
|
|
if (RC_BAD( rc = nssInitialize()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
#endif
|
|
|
|
#ifdef FLM_DBG_LOG
|
|
flmDbgLogInit();
|
|
#endif
|
|
|
|
/* Initialize all of the fields. */
|
|
|
|
FLM_SECS_TO_TIMER_UNITS( DEFAULT_MAX_UNUSED_TIME,
|
|
gv_FlmSysData.uiMaxUnusedTime);
|
|
FLM_SECS_TO_TIMER_UNITS( DEFAULT_MAX_CP_INTERVAL,
|
|
gv_FlmSysData.uiMaxCPInterval);
|
|
FLM_SECS_TO_TIMER_UNITS( DEFAULT_MAX_TRANS_SECS,
|
|
gv_FlmSysData.uiMaxTransTime);
|
|
FLM_SECS_TO_TIMER_UNITS( DEFAULT_MAX_TRANS_INACTIVE_SECS,
|
|
gv_FlmSysData.uiMaxTransInactiveTime);
|
|
|
|
#ifdef FLM_CAN_GET_PHYS_MEM
|
|
gv_FlmSysData.bDynamicCacheAdjust = TRUE;
|
|
gv_FlmSysData.uiCacheAdjustPercent = DEFAULT_CACHE_ADJUST_PERCENT;
|
|
gv_FlmSysData.uiCacheAdjustMin = DEFAULT_CACHE_ADJUST_MIN;
|
|
gv_FlmSysData.uiCacheAdjustMax = DEFAULT_CACHE_ADJUST_MAX;
|
|
gv_FlmSysData.uiCacheAdjustMinToLeave = DEFAULT_CACHE_ADJUST_MIN_TO_LEAVE;
|
|
FLM_SECS_TO_TIMER_UNITS( DEFAULT_CACHE_ADJUST_INTERVAL,
|
|
gv_FlmSysData.uiCacheAdjustInterval);
|
|
uiCacheBytes = flmGetCacheBytes( gv_FlmSysData.uiCacheAdjustPercent,
|
|
gv_FlmSysData.uiCacheAdjustMin,
|
|
gv_FlmSysData.uiCacheAdjustMax,
|
|
gv_FlmSysData.uiCacheAdjustMinToLeave, TRUE, 0);
|
|
#else
|
|
gv_FlmSysData.bDynamicCacheAdjust = FALSE;
|
|
gv_FlmSysData.uiCacheAdjustInterval = 0;
|
|
uiCacheBytes = DEFAULT_CACHE_ADJUST_MIN;
|
|
#endif
|
|
|
|
if( uiCacheBytes > FLM_MAX_CACHE_SIZE)
|
|
{
|
|
uiCacheBytes = FLM_MAX_CACHE_SIZE;
|
|
}
|
|
|
|
gv_FlmSysData.uiBlockCachePercentage = DEFAULT_BLOCK_CACHE_PERCENTAGE;
|
|
|
|
FLM_SECS_TO_TIMER_UNITS( DEFAULT_CACHE_CLEANUP_INTERVAL,
|
|
gv_FlmSysData.uiCacheCleanupInterval);
|
|
FLM_SECS_TO_TIMER_UNITS( DEFAULT_UNUSED_CLEANUP_INTERVAL,
|
|
gv_FlmSysData.uiUnusedCleanupInterval);
|
|
|
|
// Initialize the thread manager
|
|
|
|
if( (gv_FlmSysData.pThreadMgr = f_new F_ThreadMgr) == NULL)
|
|
{
|
|
rc = RC_SET( FERR_MEM);
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( rc = gv_FlmSysData.pThreadMgr->setupThreadMgr()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Initialize the serial number generator
|
|
|
|
if( RC_BAD( rc = f_initSerialNumberGenerator()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Initialize the slab manager
|
|
|
|
if( (gv_FlmSysData.pSlabManager = f_new F_SlabManager) == NULL)
|
|
{
|
|
rc = RC_SET( FERR_MEM);
|
|
goto Exit;
|
|
}
|
|
|
|
if( !gv_FlmSysData.bDynamicCacheAdjust)
|
|
{
|
|
if( RC_BAD( rc = gv_FlmSysData.pSlabManager->setup( uiCacheBytes)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( RC_BAD( rc = gv_FlmSysData.pSlabManager->setup( 0)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Divide cache bytes evenly between block and record cache.
|
|
|
|
gv_FlmSysData.uiMaxCache = uiCacheBytes;
|
|
uiCacheBytes >>= 1;
|
|
if (RC_BAD( rc = ScaInit( uiCacheBytes)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (RC_BAD( rc = flmRcaInit( uiCacheBytes)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
/* Create the mutex for controlling access to the structure. */
|
|
|
|
if (RC_BAD( rc = f_mutexCreate( &gv_FlmSysData.hShareMutex)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (RC_BAD( rc = f_mutexCreate( &gv_FlmSysData.hQueryMutex)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (RC_BAD(rc = f_mutexCreate( &gv_FlmSysData.hFileHdlMutex)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (RC_BAD(rc = f_mutexCreate( &gv_FlmSysData.hServerLockMgrMutex)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (RC_BAD( rc = f_mutexCreate( &gv_FlmSysData.HttpConfigParms.hMutex)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (RC_BAD( rc = f_mutexCreate( &gv_FlmSysData.hHttpSessionMutex)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
/* Initialize a statistics structure. */
|
|
|
|
if (RC_BAD( rc = flmStatInit( &gv_FlmSysData.Stats, TRUE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
gv_FlmSysData.bStatsInitialized = TRUE;
|
|
|
|
/* Allocate memory for the file name hash table. */
|
|
|
|
if (RC_BAD(rc = flmAllocHashTbl( FILE_HASH_ENTRIES,
|
|
&gv_FlmSysData.pFileHashTbl)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
gv_FlmSysData.uiNextFFileId = 1;
|
|
|
|
/* Allocate and Initialize FLAIM Shared File Handle Manager */
|
|
|
|
if ((gv_FlmSysData.pFileHdlMgr =
|
|
new F_FileHdlMgr( &gv_FlmSysData.hFileHdlMutex)) == NULL)
|
|
{
|
|
rc = RC_SET( FERR_MEM);
|
|
goto Exit;
|
|
}
|
|
|
|
if (RC_BAD( rc = gv_FlmSysData.pFileHdlMgr->Setup( DEFAULT_OPEN_THRESHOLD,
|
|
DEFAULT_MAX_UNUSED_TIME)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
/* Allocate and Initialize FLAIM Shared File System object */
|
|
|
|
if ((gv_FlmSysData.pFileSystem = f_new F_FileSystemImp) == NULL)
|
|
{
|
|
rc = RC_SET( FERR_MEM);
|
|
goto Exit;
|
|
}
|
|
|
|
#if defined( FLM_WIN)
|
|
OSVERSIONINFO versionInfo;
|
|
|
|
versionInfo.dwOSVersionInfoSize = sizeof( OSVERSIONINFO);
|
|
if( !GetVersionEx( &versionInfo))
|
|
{
|
|
return( MapWinErrorToFlaim( GetLastError(), FERR_FAILURE));
|
|
}
|
|
|
|
// Async writes are not supported on Win32s (3.1) or
|
|
// Win95, 98, ME, etc.
|
|
|
|
gv_FlmSysData.bOkToDoAsyncWrites =
|
|
(versionInfo.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS &&
|
|
versionInfo.dwPlatformId != VER_PLATFORM_WIN32s)
|
|
? TRUE
|
|
: FALSE;
|
|
#else
|
|
gv_FlmSysData.bOkToDoAsyncWrites = TRUE;
|
|
#endif
|
|
|
|
/* Allocate and Initialize FLAIM Server Lock Manager. */
|
|
|
|
if ((gv_FlmSysData.pServerLockMgr =
|
|
new ServerLockManager( &gv_FlmSysData.hServerLockMgrMutex)) == NULL)
|
|
{
|
|
rc = RC_SET( FERR_MEM);
|
|
goto Exit;
|
|
}
|
|
|
|
// Set up the session manager
|
|
|
|
if( (gv_FlmSysData.pSessionMgr = f_new F_SessionMgr) == NULL)
|
|
{
|
|
rc = RC_SET( FERR_MEM);
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( rc = gv_FlmSysData.pSessionMgr->setupSessionMgr()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Set up hash table for lock manager.
|
|
|
|
if (RC_BAD( rc = gv_FlmSysData.pServerLockMgr->SetupHashTbl()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Set up mutexes for the event table.
|
|
|
|
for (iEventCategory = 0;
|
|
iEventCategory < F_MAX_EVENT_CATEGORIES;
|
|
iEventCategory++)
|
|
{
|
|
if (RC_BAD( rc = f_mutexCreate(
|
|
&gv_FlmSysData.EventHdrs [iEventCategory].hMutex)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Start the monitor thread - ALWAYS DO LAST. EVERYTHING MUST BE
|
|
// SETUP PROPERLY BEFORE STARTING THIS THREAD.
|
|
|
|
if (RC_BAD( rc = f_threadCreate( &gv_FlmSysData.pMonitorThrd,
|
|
flmMonitor, "DB Monitor")))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
#ifdef FLM_USE_NICI
|
|
iHandle = f_getpid();
|
|
|
|
// Initialize NICI
|
|
if (CCS_Init(&iHandle))
|
|
{
|
|
// Failure.
|
|
rc = RC_SET( FERR_NICI_INIT_FAILED);
|
|
goto Exit;
|
|
}
|
|
#endif
|
|
|
|
Exit:
|
|
|
|
// If not successful, free up any resources that were allocated.
|
|
|
|
if (RC_BAD( rc))
|
|
{
|
|
flmCleanup();
|
|
}
|
|
flmUnlockSysData();
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine sets the limits for record cache and block cache - dividing
|
|
the total cache between the two caches. It uses the same ratio
|
|
currently in force.
|
|
****************************************************************************/
|
|
FSTATIC RCODE flmSetCacheLimits(
|
|
FLMUINT uiNewTotalCacheSize,
|
|
FLMBOOL bPreallocateCache)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FLMUINT uiNewBlockCacheSize;
|
|
FLMBOOL bResizeAfterConfig = FALSE;
|
|
|
|
if( uiNewTotalCacheSize > FLM_MAX_CACHE_SIZE)
|
|
{
|
|
uiNewTotalCacheSize = FLM_MAX_CACHE_SIZE;
|
|
}
|
|
|
|
if( gv_FlmSysData.bDynamicCacheAdjust || !bPreallocateCache)
|
|
{
|
|
DONT_PREALLOCATE:
|
|
|
|
if( uiNewTotalCacheSize < gv_FlmSysData.uiMaxCache)
|
|
{
|
|
bResizeAfterConfig = TRUE;
|
|
}
|
|
else
|
|
{
|
|
if( RC_BAD( rc = gv_FlmSysData.pSlabManager->resize( 0)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
gv_FlmSysData.bCachePreallocated = FALSE;
|
|
}
|
|
else
|
|
{
|
|
if( RC_BAD( rc = gv_FlmSysData.pSlabManager->resize(
|
|
uiNewTotalCacheSize, &uiNewTotalCacheSize)))
|
|
{
|
|
|
|
// Log a message indicating that we couldn't pre-allocate
|
|
// the cache
|
|
|
|
flmLogMessage(
|
|
FLM_DEBUG_MESSAGE,
|
|
FLM_YELLOW,
|
|
FLM_BLACK,
|
|
"WARNING: Couldn't pre-allocate cache.");
|
|
|
|
goto DONT_PREALLOCATE;
|
|
}
|
|
|
|
gv_FlmSysData.bCachePreallocated = TRUE;
|
|
}
|
|
|
|
if( gv_FlmSysData.uiBlockCachePercentage == 100)
|
|
{
|
|
uiNewBlockCacheSize = uiNewTotalCacheSize;
|
|
}
|
|
else
|
|
{
|
|
uiNewBlockCacheSize = (FLMUINT)((uiNewTotalCacheSize / 100) *
|
|
gv_FlmSysData.uiBlockCachePercentage);
|
|
}
|
|
|
|
if (RC_OK( rc = ScaConfig( FLM_CACHE_LIMIT,
|
|
(void *)uiNewBlockCacheSize, (void *)0)))
|
|
{
|
|
rc = flmRcaConfig( FLM_CACHE_LIMIT,
|
|
(void *)(uiNewTotalCacheSize - uiNewBlockCacheSize),
|
|
(void *)0);
|
|
}
|
|
|
|
if( bResizeAfterConfig)
|
|
{
|
|
(void)gv_FlmSysData.pSlabManager->resize( 0);
|
|
}
|
|
|
|
gv_FlmSysData.uiMaxCache = uiNewTotalCacheSize;
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/*API~***********************************************************************
|
|
Desc : Configures how memory will be dynamically regulated.
|
|
*END************************************************************************/
|
|
RCODE FlmSetDynamicMemoryLimit(
|
|
FLMUINT uiCacheAdjustPercent,
|
|
// [IN] Percentage of available physical memory to set or adjust to.
|
|
FLMUINT uiCacheAdjustMin,
|
|
// [IN] Minimum bytes to adjust down to.
|
|
FLMUINT uiCacheAdjustMax,
|
|
// [IN] Maximum bytes to adjust up to.
|
|
FLMUINT uiCacheAdjustMinToLeave
|
|
// [IN] Minimum bytes to leave available after making adjustment.
|
|
)
|
|
{
|
|
#ifndef FLM_CAN_GET_PHYS_MEM
|
|
F_UNREFERENCED_PARM( uiCacheAdjustPercent);
|
|
F_UNREFERENCED_PARM( uiCacheAdjustMin);
|
|
F_UNREFERENCED_PARM( uiCacheAdjustMax);
|
|
F_UNREFERENCED_PARM( uiCacheAdjustMinToLeave);
|
|
return( RC_SET( FERR_NOT_IMPLEMENTED));
|
|
#else
|
|
RCODE rc = FERR_OK;
|
|
FLMUINT uiCacheBytes;
|
|
|
|
f_mutexLock( gv_FlmSysData.hShareMutex);
|
|
f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex);
|
|
gv_FlmSysData.bDynamicCacheAdjust = TRUE;
|
|
flmAssert( uiCacheAdjustPercent > 0 &&
|
|
uiCacheAdjustPercent <= 100);
|
|
gv_FlmSysData.uiCacheAdjustPercent = uiCacheAdjustPercent;
|
|
gv_FlmSysData.uiCacheAdjustMin = uiCacheAdjustMin;
|
|
gv_FlmSysData.uiCacheAdjustMax = uiCacheAdjustMax;
|
|
gv_FlmSysData.uiCacheAdjustMinToLeave = uiCacheAdjustMinToLeave;
|
|
uiCacheBytes = flmGetCacheBytes( gv_FlmSysData.uiCacheAdjustPercent,
|
|
gv_FlmSysData.uiCacheAdjustMin,
|
|
gv_FlmSysData.uiCacheAdjustMax,
|
|
gv_FlmSysData.uiCacheAdjustMinToLeave, TRUE,
|
|
gv_FlmSysData.SCacheMgr.Usage.uiTotalBytesAllocated +
|
|
gv_FlmSysData.RCacheMgr.pRCacheAlloc->getTotalBytesAllocated());
|
|
rc = flmSetCacheLimits( uiCacheBytes, FALSE);
|
|
f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex);
|
|
f_mutexUnlock( gv_FlmSysData.hShareMutex);
|
|
return( rc);
|
|
#endif
|
|
}
|
|
|
|
/*API~***********************************************************************
|
|
Desc : Sets a hard memory limit for cache.
|
|
*END************************************************************************/
|
|
RCODE FlmSetHardMemoryLimit(
|
|
FLMUINT uiPercent,
|
|
// [IN] Percentage of available physical memory or total physical
|
|
// memory to set limit to. If zero, uiMax is used as a byte
|
|
// limit.
|
|
FLMBOOL bPercentOfAvail,
|
|
// [IN] Percentage is to be interpreted as a percentage of available
|
|
// memory as opposed to a percentage of all of physical memory.
|
|
FLMUINT uiMin,
|
|
// [IN] Minimum bytes to adjust down to.
|
|
FLMUINT uiMax,
|
|
// [IN] Maximum bytes to adjust up to.
|
|
FLMUINT uiMinToLeave,
|
|
// [IN] Minimum bytes to leave available after making adjustment.
|
|
FLMBOOL bPreallocate
|
|
)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
|
|
f_mutexLock( gv_FlmSysData.hShareMutex);
|
|
f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex);
|
|
gv_FlmSysData.bDynamicCacheAdjust = FALSE;
|
|
if (uiPercent)
|
|
{
|
|
#ifndef FLM_CAN_GET_PHYS_MEM
|
|
F_UNREFERENCED_PARM( bPercentOfAvail);
|
|
F_UNREFERENCED_PARM( uiMin);
|
|
F_UNREFERENCED_PARM( uiMinToLeave);
|
|
rc = RC_SET( FERR_NOT_IMPLEMENTED);
|
|
#else
|
|
FLMUINT uiCacheBytes;
|
|
|
|
uiCacheBytes = flmGetCacheBytes( uiPercent, uiMin, uiMax, uiMinToLeave,
|
|
bPercentOfAvail,
|
|
gv_FlmSysData.SCacheMgr.Usage.uiTotalBytesAllocated +
|
|
gv_FlmSysData.RCacheMgr.pRCacheAlloc->getTotalBytesAllocated());
|
|
rc = flmSetCacheLimits( uiCacheBytes, bPreallocate);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
rc = flmSetCacheLimits( uiMax, bPreallocate);
|
|
}
|
|
f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex);
|
|
f_mutexUnlock( gv_FlmSysData.hShareMutex);
|
|
return( rc);
|
|
}
|
|
|
|
/*API~***********************************************************************
|
|
Desc : Returns information about memory usage.
|
|
*END************************************************************************/
|
|
void FlmGetMemoryInfo(
|
|
FLM_MEM_INFO * pMemInfo)
|
|
{
|
|
f_memset( pMemInfo, 0, sizeof( FLM_MEM_INFO));
|
|
f_mutexLock( gv_FlmSysData.hShareMutex);
|
|
f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex);
|
|
pMemInfo->bDynamicCacheAdjust = gv_FlmSysData.bDynamicCacheAdjust;
|
|
pMemInfo->uiCacheAdjustPercent = gv_FlmSysData.uiCacheAdjustPercent;
|
|
pMemInfo->uiCacheAdjustMin = gv_FlmSysData.uiCacheAdjustMin;
|
|
pMemInfo->uiCacheAdjustMax = gv_FlmSysData.uiCacheAdjustMax;
|
|
pMemInfo->uiCacheAdjustMinToLeave = gv_FlmSysData.uiCacheAdjustMinToLeave;
|
|
|
|
// Return record cache information.
|
|
|
|
f_memcpy( &pMemInfo->RecordCache, &gv_FlmSysData.RCacheMgr.Usage,
|
|
sizeof( FLM_CACHE_USAGE));
|
|
|
|
// Return block cache information.
|
|
|
|
f_memcpy( &pMemInfo->BlockCache, &gv_FlmSysData.SCacheMgr.Usage,
|
|
sizeof( FLM_CACHE_USAGE));
|
|
|
|
pMemInfo->uiFreeBytes = gv_FlmSysData.SCacheMgr.uiFreeBytes;
|
|
pMemInfo->uiFreeCount = gv_FlmSysData.SCacheMgr.uiFreeCount;
|
|
pMemInfo->uiReplaceableCount = gv_FlmSysData.SCacheMgr.uiReplaceableCount;
|
|
pMemInfo->uiReplaceableBytes = gv_FlmSysData.SCacheMgr.uiReplaceableBytes;
|
|
|
|
if( gv_FlmSysData.pFileHashTbl)
|
|
{
|
|
FLMUINT uiLoop;
|
|
FFILE * pFile;
|
|
|
|
for( uiLoop = 0; uiLoop < FILE_HASH_ENTRIES; uiLoop++)
|
|
{
|
|
if( (pFile = (FFILE *)gv_FlmSysData.pFileHashTbl[
|
|
uiLoop].pFirstInBucket) != NULL)
|
|
{
|
|
while( pFile)
|
|
{
|
|
if( pFile->pECacheMgr)
|
|
{
|
|
pFile->pECacheMgr->getStats( &pMemInfo->ECache, TRUE);
|
|
}
|
|
|
|
if( pFile->uiDirtyCacheCount)
|
|
{
|
|
pMemInfo->uiDirtyBytes +=
|
|
pFile->uiDirtyCacheCount * pFile->FileHdr.uiBlockSize;
|
|
pMemInfo->uiDirtyCount += pFile->uiDirtyCacheCount;
|
|
}
|
|
|
|
if( pFile->uiNewCount)
|
|
{
|
|
pMemInfo->uiNewBytes +=
|
|
pFile->uiNewCount * pFile->FileHdr.uiBlockSize;
|
|
pMemInfo->uiNewCount += pFile->uiNewCount;
|
|
}
|
|
|
|
if( pFile->uiLogCacheCount)
|
|
{
|
|
pMemInfo->uiLogBytes +=
|
|
pFile->uiLogCacheCount * pFile->FileHdr.uiBlockSize;
|
|
pMemInfo->uiLogCount += pFile->uiLogCacheCount;
|
|
}
|
|
|
|
pFile = pFile->pNext;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex);
|
|
f_mutexUnlock( gv_FlmSysData.hShareMutex);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc : Close a database file - all background threads are closed too.
|
|
****************************************************************************/
|
|
FSTATIC RCODE flmCloseDbFile(
|
|
const char * pszDbFileName,
|
|
const char * pszDataDir)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FLMBOOL bMutexLocked = FALSE;
|
|
FFILE * pFile;
|
|
|
|
f_mutexLock( gv_FlmSysData.hShareMutex);
|
|
bMutexLocked = TRUE;
|
|
|
|
Retry:
|
|
|
|
// Look up the file using flmFindFile to see if we have the
|
|
// file open. May unlock and re-lock the global mutex.
|
|
|
|
if (RC_BAD( rc = flmFindFile( pszDbFileName, pszDataDir, &pFile)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// If we did not find the file, we are OK.
|
|
|
|
if (!pFile)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// If the file is in the process of being opened by another
|
|
// thread, wait for the open to complete.
|
|
|
|
if (pFile->uiFlags & DBF_BEING_OPENED)
|
|
{
|
|
if (RC_BAD( rc = flmWaitNotifyReq( gv_FlmSysData.hShareMutex,
|
|
&pFile->pOpenNotifies,
|
|
(void *)0)))
|
|
{
|
|
// GW Bug #24307. If flmWaitNotifyReq returns a bad RC, assume that
|
|
// the other thread will unlock and free the pFile structure. This
|
|
// routine should only unlock the pFile if an error occurs at some
|
|
// other point. See flmVerifyFileUse.
|
|
|
|
// *ppFileRV is set to NULL at Exit.
|
|
goto Exit;
|
|
}
|
|
|
|
goto Retry;
|
|
}
|
|
|
|
if( pFile->uiUseCount)
|
|
{
|
|
// Increment the use count temporarily so that the FFILE won't be
|
|
// moved to the NU list because of db close calls made by
|
|
// releaseFileResources.
|
|
|
|
pFile->uiUseCount++;
|
|
|
|
// Must unlock the mutex prior to calling releaseFileResources because
|
|
// it may call API-level routines (such as FlmDbClose) that do not
|
|
// expect the mutex to be locked.
|
|
|
|
f_mutexUnlock( gv_FlmSysData.hShareMutex);
|
|
bMutexLocked = FALSE;
|
|
|
|
// First, all session resources are released that are
|
|
// associated with the specified database
|
|
|
|
if( gv_FlmSysData.pSessionMgr)
|
|
{
|
|
gv_FlmSysData.pSessionMgr->releaseFileResources( pFile);
|
|
}
|
|
|
|
f_mutexLock( gv_FlmSysData.hShareMutex);
|
|
bMutexLocked = TRUE;
|
|
|
|
// Decrement the temporary use count that was added above.
|
|
// By now, the FFILE may no longer be in use. If so, we need to
|
|
// link it to the NU list.
|
|
|
|
if (!(--pFile->uiUseCount))
|
|
{
|
|
// If the "must close" flag is set, it indicates that
|
|
// the FFILE is being forced to close. Put the FFILE in
|
|
// the NU list, but specify that it should be quickly
|
|
// timed-out. We link the file to the NU list even
|
|
// though we are going to unlink it below because
|
|
// flmLinkFileToNUList closes the RFL file(s).
|
|
|
|
flmLinkFileToNUList( pFile, pFile->bMustClose);
|
|
}
|
|
}
|
|
|
|
// If the FFILE is in the NU list, unlink it
|
|
|
|
flmUnlinkFileFromNUList( pFile);
|
|
|
|
// If we have non-background threads accessing the database,
|
|
// we cannot close the file.
|
|
|
|
if (pFile->uiUseCount > pFile->uiInternalUseCount)
|
|
{
|
|
rc = RC_SET( FERR_TRANS_ACTIVE);
|
|
goto Exit;
|
|
}
|
|
|
|
// Close the file.
|
|
|
|
flmFreeFile( pFile);
|
|
|
|
// Unlock the mutex
|
|
|
|
f_mutexUnlock( gv_FlmSysData.hShareMutex);
|
|
bMutexLocked = FALSE;
|
|
|
|
// Clean up any unused file handles
|
|
|
|
(void)gv_FlmSysData.pFileHdlMgr->CheckAgedItems( (FLMUINT)0);
|
|
|
|
Exit:
|
|
|
|
if (bMutexLocked)
|
|
{
|
|
f_mutexUnlock( gv_FlmSysData.hShareMutex);
|
|
}
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc : Register our http callback function
|
|
****************************************************************************/
|
|
FSTATIC RCODE flmRegisterHttpCallback(
|
|
FLM_MODULE_HANDLE hModule,
|
|
const char * pszUrlString)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
char * pszTemp;
|
|
FLMUINT uiRegFlags;
|
|
|
|
if (gv_FlmSysData.HttpConfigParms.bRegistered)
|
|
{
|
|
rc = RC_SET( FERR_HTTP_REGISTER_FAILURE);
|
|
goto Exit;
|
|
}
|
|
|
|
// Need to save the Url string for later use...
|
|
|
|
if( RC_BAD( rc = f_alloc( f_strlen( pszUrlString) + 1, &pszTemp)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
f_strcpy( pszTemp, pszUrlString);
|
|
|
|
// Set the flags that tell the server what kind of authentication
|
|
// we want:
|
|
// HR_STK_BOTH = Allow both http and https
|
|
// HR_AUTH_USERSA = Allow any user in the NDS tree or the SAdmin user
|
|
// HR_REALM_NDS = Use the NDS realm for authentication
|
|
|
|
uiRegFlags = HR_STK_BOTH | HR_AUTH_USERSA | HR_REALM_NDS;
|
|
|
|
if (gv_FlmSysData.HttpConfigParms.fnReg)
|
|
{
|
|
if( gv_FlmSysData.HttpConfigParms.fnReg( hModule, pszUrlString,
|
|
(FLMUINT32)uiRegFlags, flmHttpCallback, NULL, NULL) != 0)
|
|
{
|
|
rc = RC_SET( FERR_HTTP_REGISTER_FAILURE);
|
|
goto Exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
flmAssert( 0);
|
|
rc = RC_SET( FERR_NO_HTTP_STACK);
|
|
goto Exit;
|
|
}
|
|
|
|
// Save the URL string in gv_FlmSysData
|
|
|
|
gv_FlmSysData.HttpConfigParms.pszURLString = pszTemp;
|
|
gv_FlmSysData.HttpConfigParms.uiURLStringLen = f_strlen( pszTemp);
|
|
pszTemp = NULL;
|
|
|
|
gv_FlmSysData.HttpConfigParms.bRegistered = TRUE;
|
|
|
|
Exit:
|
|
|
|
if (RC_BAD( rc))
|
|
{
|
|
if (pszTemp)
|
|
{
|
|
f_free( &pszTemp);
|
|
pszTemp = NULL;
|
|
}
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc : Deregister our http callback function
|
|
****************************************************************************/
|
|
FSTATIC RCODE flmDeregisterHttpCallback()
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
if (!gv_FlmSysData.HttpConfigParms.bRegistered)
|
|
{
|
|
rc = RC_SET( FERR_HTTP_DEREG_FAILURE);
|
|
goto Exit;
|
|
}
|
|
|
|
if ( !gv_FlmSysData.HttpConfigParms.fnDereg ||
|
|
!gv_FlmSysData.HttpConfigParms.pszURLString )
|
|
{
|
|
flmAssert( 0);
|
|
rc = RC_SET( FERR_NO_HTTP_STACK);
|
|
goto Exit;
|
|
}
|
|
|
|
if ( gv_FlmSysData.HttpConfigParms.fnDereg(
|
|
gv_FlmSysData.HttpConfigParms.pszURLString,
|
|
flmHttpCallback) != 0)
|
|
{
|
|
rc = RC_SET( FERR_HTTP_DEREG_FAILURE);
|
|
goto Exit;
|
|
}
|
|
|
|
if( gv_FlmSysData.HttpConfigParms.pszURLString)
|
|
{
|
|
f_free( &gv_FlmSysData.HttpConfigParms.pszURLString);
|
|
gv_FlmSysData.HttpConfigParms.pszURLString = NULL;
|
|
}
|
|
|
|
// Now, tell the callback function to delete the webpage factory and cleanup
|
|
// the allocated memory etc...
|
|
f_mutexLock( gv_FlmSysData.HttpConfigParms.hMutex);
|
|
while (gv_FlmSysData.HttpConfigParms.uiUseCount)
|
|
{
|
|
f_mutexUnlock( gv_FlmSysData.HttpConfigParms.hMutex);
|
|
f_sleep( 10);
|
|
f_mutexLock( gv_FlmSysData.HttpConfigParms.hMutex);
|
|
}
|
|
flmHttpCallback( NULL, NULL);
|
|
f_mutexUnlock( gv_FlmSysData.HttpConfigParms.hMutex);
|
|
|
|
|
|
gv_FlmSysData.HttpConfigParms.bRegistered = FALSE;
|
|
Exit:
|
|
return( rc);
|
|
}
|
|
|
|
|
|
/*API~***********************************************************************
|
|
Desc: Configures share attributes.
|
|
*END************************************************************************/
|
|
RCODE FlmConfig(
|
|
eFlmConfigTypes eConfigType,
|
|
void * Value1,
|
|
void * Value2)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FLMUINT uiValue;
|
|
FLMUINT uiSave;
|
|
FLMUINT uiCurrTime;
|
|
FLMUINT uiSaveMax;
|
|
|
|
switch( eConfigType)
|
|
{
|
|
|
|
case FLM_OPEN_THRESHOLD:
|
|
uiValue = (FLMUINT) Value1;
|
|
rc = gv_FlmSysData.pFileHdlMgr->SetOpenThreshold( uiValue);
|
|
break;
|
|
|
|
case FLM_CACHE_LIMIT:
|
|
{
|
|
f_mutexLock( gv_FlmSysData.hShareMutex);
|
|
f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex);
|
|
gv_FlmSysData.bDynamicCacheAdjust = FALSE;
|
|
rc = flmSetCacheLimits( (FLMUINT)Value1, (FLMBOOL)Value2);
|
|
f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex);
|
|
f_mutexUnlock( gv_FlmSysData.hShareMutex);
|
|
break;
|
|
}
|
|
|
|
case FLM_BLOCK_CACHE_PERCENTAGE:
|
|
{
|
|
f_mutexLock( gv_FlmSysData.hShareMutex);
|
|
f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex);
|
|
|
|
if( (gv_FlmSysData.uiBlockCachePercentage = (FLMUINT)Value1) > 100)
|
|
{
|
|
gv_FlmSysData.uiBlockCachePercentage = 100;
|
|
}
|
|
|
|
rc = flmSetCacheLimits(
|
|
gv_FlmSysData.SCacheMgr.Usage.uiMaxBytes +
|
|
gv_FlmSysData.RCacheMgr.Usage.uiMaxBytes,
|
|
gv_FlmSysData.bCachePreallocated);
|
|
|
|
f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex);
|
|
f_mutexUnlock( gv_FlmSysData.hShareMutex);
|
|
break;
|
|
}
|
|
|
|
case FLM_SCACHE_DEBUG:
|
|
f_mutexLock( gv_FlmSysData.hShareMutex);
|
|
f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex);
|
|
if (RC_OK( rc = ScaConfig( FLM_SCACHE_DEBUG, Value1, Value2)))
|
|
{
|
|
rc = flmRcaConfig( FLM_SCACHE_DEBUG, Value1, Value2);
|
|
}
|
|
f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex);
|
|
f_mutexUnlock( gv_FlmSysData.hShareMutex);
|
|
break;
|
|
|
|
case FLM_CLOSE_UNUSED_FILES:
|
|
|
|
// Timeout inactive sessions
|
|
|
|
if( gv_FlmSysData.pSessionMgr)
|
|
{
|
|
gv_FlmSysData.pSessionMgr->timeoutInactiveSessions(
|
|
(FLMUINT)Value1, TRUE);
|
|
}
|
|
|
|
// Convert seconds to timer units
|
|
|
|
FLM_SECS_TO_TIMER_UNITS( (FLMUINT) Value1, uiValue);
|
|
rc = gv_FlmSysData.pFileHdlMgr->CheckAgedItems( uiValue);
|
|
|
|
// Free any other unused structures that have not been used for the
|
|
// specified amount of time.
|
|
|
|
uiCurrTime = (FLMUINT)FLM_GET_TIMER();
|
|
f_mutexLock( gv_FlmSysData.hShareMutex);
|
|
|
|
// Temporarily set the maximum unused seconds in the FLMSYSDATA structure
|
|
// to the value that was passed in to Value1. Restore it after
|
|
// calling flmCheckNUStructs.
|
|
|
|
uiSave = gv_FlmSysData.uiMaxUnusedTime;
|
|
gv_FlmSysData.uiMaxUnusedTime = uiValue;
|
|
|
|
// May unlock and re-lock the global mutex.
|
|
flmCheckNUStructs( uiCurrTime);
|
|
gv_FlmSysData.uiMaxUnusedTime = uiSave;
|
|
f_mutexUnlock( gv_FlmSysData.hShareMutex);
|
|
break;
|
|
|
|
case FLM_CLOSE_ALL_FILES:
|
|
rc = flmCloseAllFiles();
|
|
break;
|
|
|
|
case FLM_START_STATS:
|
|
(void)flmStatStart( &gv_FlmSysData.Stats);
|
|
|
|
// Start query statistics, if they have not
|
|
// already been started.
|
|
|
|
f_mutexLock( gv_FlmSysData.hQueryMutex);
|
|
if (!gv_FlmSysData.uiMaxQueries)
|
|
{
|
|
gv_FlmSysData.uiMaxQueries = 20;
|
|
gv_FlmSysData.bNeedToUnsetMaxQueries = TRUE;
|
|
}
|
|
f_mutexUnlock( gv_FlmSysData.hQueryMutex);
|
|
break;
|
|
|
|
case FLM_STOP_STATS:
|
|
(void)flmStatStop( &gv_FlmSysData.Stats);
|
|
|
|
// Stop query statistics, if they were
|
|
// started by a call to FLM_START_STATS.
|
|
|
|
f_mutexLock( gv_FlmSysData.hQueryMutex);
|
|
if (gv_FlmSysData.bNeedToUnsetMaxQueries)
|
|
{
|
|
gv_FlmSysData.uiMaxQueries = 0;
|
|
flmFreeSavedQueries( TRUE);
|
|
// NOTE: flmFreeSavedQueries unlocks the mutex.
|
|
}
|
|
else
|
|
{
|
|
f_mutexUnlock( gv_FlmSysData.hQueryMutex);
|
|
}
|
|
break;
|
|
|
|
case FLM_RESET_STATS:
|
|
|
|
// Lock the record cache manager's mutex
|
|
|
|
f_mutexLock( gv_FlmSysData.hShareMutex);
|
|
f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex);
|
|
|
|
// Reset the record cache statistics
|
|
|
|
gv_FlmSysData.RCacheMgr.uiIoWaits = 0;
|
|
gv_FlmSysData.RCacheMgr.Usage.uiCacheHits = 0;
|
|
gv_FlmSysData.RCacheMgr.Usage.uiCacheHitLooks = 0;
|
|
gv_FlmSysData.RCacheMgr.Usage.uiCacheFaults = 0;
|
|
gv_FlmSysData.RCacheMgr.Usage.uiCacheFaultLooks = 0;
|
|
|
|
// Reset the block cache statistics.
|
|
|
|
gv_FlmSysData.SCacheMgr.uiIoWaits = 0;
|
|
gv_FlmSysData.SCacheMgr.Usage.uiCacheHits = 0;
|
|
gv_FlmSysData.SCacheMgr.Usage.uiCacheHitLooks = 0;
|
|
gv_FlmSysData.SCacheMgr.Usage.uiCacheFaults = 0;
|
|
gv_FlmSysData.SCacheMgr.Usage.uiCacheFaultLooks = 0;
|
|
|
|
// Unlock the cache manager's mutex
|
|
|
|
f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex);
|
|
f_mutexUnlock( gv_FlmSysData.hShareMutex);
|
|
|
|
(void)flmStatReset( &gv_FlmSysData.Stats, FALSE, TRUE);
|
|
|
|
f_mutexLock( gv_FlmSysData.hQueryMutex);
|
|
uiSaveMax = gv_FlmSysData.uiMaxQueries;
|
|
gv_FlmSysData.uiMaxQueries = 0;
|
|
flmFreeSavedQueries( TRUE);
|
|
// NOTE: flmFreeSavedQueries unlocks the mutex.
|
|
|
|
// Restore the old maximum
|
|
|
|
if (uiSaveMax)
|
|
{
|
|
|
|
// flmFreeSavedQueries unlocks the mutex, so we
|
|
// must relock it to restore the old maximum.
|
|
|
|
f_mutexLock( gv_FlmSysData.hQueryMutex);
|
|
gv_FlmSysData.uiMaxQueries = uiSaveMax;
|
|
f_mutexUnlock( gv_FlmSysData.hQueryMutex);
|
|
}
|
|
break;
|
|
|
|
case FLM_TMPDIR:
|
|
f_mutexLock( gv_FlmSysData.hShareMutex);
|
|
rc = flmSetTmpDir( (const char *)Value1);
|
|
f_mutexUnlock( gv_FlmSysData.hShareMutex);
|
|
break;
|
|
|
|
case FLM_MAX_CP_INTERVAL:
|
|
FLM_SECS_TO_TIMER_UNITS( (FLMUINT) Value1, gv_FlmSysData.uiMaxCPInterval);
|
|
break;
|
|
|
|
case FLM_BLOB_EXT:
|
|
{
|
|
const char * pszTmp = (const char *)Value1;
|
|
|
|
if (!pszTmp)
|
|
{
|
|
gv_FlmSysData.ucBlobExt [0] = 0;
|
|
}
|
|
else
|
|
{
|
|
int iCnt;
|
|
|
|
// Don't save any more than 63 characters.
|
|
|
|
for (iCnt = 0;
|
|
((iCnt < 63) && (*pszTmp));
|
|
iCnt++, pszTmp++)
|
|
{
|
|
gv_FlmSysData.ucBlobExt [iCnt] = *pszTmp;
|
|
}
|
|
gv_FlmSysData.ucBlobExt [iCnt] = 0;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case FLM_MAX_TRANS_SECS:
|
|
FLM_SECS_TO_TIMER_UNITS( (FLMUINT) Value1,
|
|
gv_FlmSysData.uiMaxTransTime);
|
|
break;
|
|
|
|
case FLM_MAX_TRANS_INACTIVE_SECS:
|
|
FLM_SECS_TO_TIMER_UNITS( (FLMUINT) Value1,
|
|
gv_FlmSysData.uiMaxTransInactiveTime);
|
|
break;
|
|
|
|
case FLM_CACHE_ADJUST_INTERVAL:
|
|
FLM_SECS_TO_TIMER_UNITS( (FLMUINT)Value1,
|
|
gv_FlmSysData.uiCacheAdjustInterval);
|
|
break;
|
|
|
|
case FLM_CACHE_CLEANUP_INTERVAL:
|
|
FLM_SECS_TO_TIMER_UNITS( (FLMUINT)Value1,
|
|
gv_FlmSysData.uiCacheCleanupInterval);
|
|
break;
|
|
|
|
case FLM_UNUSED_CLEANUP_INTERVAL:
|
|
FLM_SECS_TO_TIMER_UNITS( (FLMUINT)Value1,
|
|
gv_FlmSysData.uiUnusedCleanupInterval);
|
|
break;
|
|
|
|
case FLM_MAX_UNUSED_TIME:
|
|
f_mutexLock( gv_FlmSysData.hShareMutex);
|
|
FLM_SECS_TO_TIMER_UNITS( (FLMUINT)Value1,
|
|
gv_FlmSysData.uiMaxUnusedTime);
|
|
f_mutexUnlock( gv_FlmSysData.hShareMutex);
|
|
break;
|
|
|
|
case FLM_OUT_OF_MEM_SIMULATION:
|
|
#ifdef DEBUG_SIM_OUT_OF_MEM
|
|
gv_FlmSysData.uiOutOfMemSimEnabledFlag =
|
|
(FLMUINT)((Value1) ? OUT_OF_MEM_SIM_ENABLED_FLAG : 0);
|
|
#else
|
|
rc = RC_SET( FERR_NOT_IMPLEMENTED);
|
|
#endif
|
|
break;
|
|
|
|
case FLM_CACHE_CHECK:
|
|
gv_FlmSysData.bCheckCache = (FLMBOOL)((Value1 != 0)
|
|
? (FLMBOOL)TRUE
|
|
: (FLMBOOL)FALSE);
|
|
break;
|
|
|
|
case FLM_CLOSE_FILE:
|
|
rc = flmCloseDbFile( (const char *)Value1, (const char *)Value2);
|
|
break;
|
|
|
|
case FLM_LOGGER:
|
|
f_mutexLock( gv_FlmSysData.hShareMutex);
|
|
if( !gv_FlmSysData.pLogger && Value1)
|
|
{
|
|
gv_FlmSysData.pLogger = (F_Logger *)Value1;
|
|
gv_FlmSysData.pLogger->lockLogger();
|
|
gv_FlmSysData.pLogger->AddRef();
|
|
gv_FlmSysData.pLogger->unlockLogger();
|
|
}
|
|
f_mutexUnlock( gv_FlmSysData.hShareMutex);
|
|
break;
|
|
|
|
case FLM_USE_ESM:
|
|
gv_FlmSysData.bOkToUseESM = (FLMBOOL)((Value1 != 0)
|
|
? (FLMBOOL)TRUE
|
|
: (FLMBOOL)FALSE);
|
|
break;
|
|
|
|
case FLM_ASSIGN_HTTP_SYMS:
|
|
//Note: before you attempt this, you had better have loaded the
|
|
//necessary shared library...
|
|
if ( gv_FlmSysData.HttpConfigParms.fnReg ||
|
|
gv_FlmSysData.HttpConfigParms.fnDereg ||
|
|
gv_FlmSysData.HttpConfigParms.fnReqPath ||
|
|
gv_FlmSysData.HttpConfigParms.fnReqQuery ||
|
|
gv_FlmSysData.HttpConfigParms.fnReqHdrValue ||
|
|
gv_FlmSysData.HttpConfigParms.fnSetHdrValue ||
|
|
gv_FlmSysData.HttpConfigParms.fnPrintf ||
|
|
gv_FlmSysData.HttpConfigParms.fnEmit ||
|
|
gv_FlmSysData.HttpConfigParms.fnSetNoCache ||
|
|
gv_FlmSysData.HttpConfigParms.fnSendHeader ||
|
|
gv_FlmSysData.HttpConfigParms.fnSetIOMode ||
|
|
gv_FlmSysData.HttpConfigParms.fnSendBuffer ||
|
|
gv_FlmSysData.HttpConfigParms.fnAcquireSession ||
|
|
gv_FlmSysData.HttpConfigParms.fnReleaseSession ||
|
|
gv_FlmSysData.HttpConfigParms.fnAcquireUser ||
|
|
gv_FlmSysData.HttpConfigParms.fnReleaseUser ||
|
|
gv_FlmSysData.HttpConfigParms.fnSetSessionValue ||
|
|
gv_FlmSysData.HttpConfigParms.fnGetSessionValue ||
|
|
gv_FlmSysData.HttpConfigParms.fnGetGblValue ||
|
|
gv_FlmSysData.HttpConfigParms.fnSetGblValue ||
|
|
gv_FlmSysData.HttpConfigParms.fnRecvBuffer )
|
|
{
|
|
rc = RC_SET( FERR_HTTP_SYMS_EXIST);
|
|
goto Exit;
|
|
}
|
|
else
|
|
{
|
|
gv_FlmSysData.HttpConfigParms.fnReg = ((HTTPCONFIGPARAMS *)Value1)->fnReg;
|
|
gv_FlmSysData.HttpConfigParms.fnDereg = ((HTTPCONFIGPARAMS *)Value1)->fnDereg;
|
|
gv_FlmSysData.HttpConfigParms.fnReqPath = ((HTTPCONFIGPARAMS *)Value1)->fnReqPath;
|
|
gv_FlmSysData.HttpConfigParms.fnReqQuery = ((HTTPCONFIGPARAMS *)Value1)->fnReqQuery;
|
|
gv_FlmSysData.HttpConfigParms.fnReqHdrValue = ((HTTPCONFIGPARAMS *)Value1)->fnReqHdrValue;
|
|
gv_FlmSysData.HttpConfigParms.fnSetHdrValue = ((HTTPCONFIGPARAMS *)Value1)->fnSetHdrValue;
|
|
gv_FlmSysData.HttpConfigParms.fnPrintf = ((HTTPCONFIGPARAMS *)Value1)->fnPrintf;
|
|
gv_FlmSysData.HttpConfigParms.fnEmit = ((HTTPCONFIGPARAMS *)Value1)->fnEmit;
|
|
gv_FlmSysData.HttpConfigParms.fnSetNoCache = ((HTTPCONFIGPARAMS *)Value1)->fnSetNoCache;
|
|
gv_FlmSysData.HttpConfigParms.fnSendHeader = ((HTTPCONFIGPARAMS *)Value1)->fnSendHeader;
|
|
gv_FlmSysData.HttpConfigParms.fnSetIOMode = ((HTTPCONFIGPARAMS *)Value1)->fnSetIOMode;
|
|
gv_FlmSysData.HttpConfigParms.fnSendBuffer = ((HTTPCONFIGPARAMS *)Value1)->fnSendBuffer;
|
|
gv_FlmSysData.HttpConfigParms.fnAcquireSession = ((HTTPCONFIGPARAMS *)Value1)->fnAcquireSession;
|
|
gv_FlmSysData.HttpConfigParms.fnReleaseSession = ((HTTPCONFIGPARAMS *)Value1)->fnReleaseSession;
|
|
gv_FlmSysData.HttpConfigParms.fnAcquireUser = ((HTTPCONFIGPARAMS *)Value1)->fnAcquireUser;
|
|
gv_FlmSysData.HttpConfigParms.fnReleaseUser = ((HTTPCONFIGPARAMS *)Value1)->fnReleaseUser;
|
|
gv_FlmSysData.HttpConfigParms.fnSetSessionValue = ((HTTPCONFIGPARAMS *)Value1)->fnSetSessionValue;
|
|
gv_FlmSysData.HttpConfigParms.fnGetSessionValue = ((HTTPCONFIGPARAMS *)Value1)->fnGetSessionValue;
|
|
gv_FlmSysData.HttpConfigParms.fnGetGblValue = ((HTTPCONFIGPARAMS *)Value1)->fnGetGblValue;
|
|
gv_FlmSysData.HttpConfigParms.fnSetGblValue = ((HTTPCONFIGPARAMS *)Value1)->fnSetGblValue;
|
|
gv_FlmSysData.HttpConfigParms.fnRecvBuffer = ((HTTPCONFIGPARAMS *)Value1)->fnRecvBuffer;
|
|
}
|
|
break;
|
|
|
|
case FLM_REGISTER_HTTP_URL:
|
|
// Value1: FLM_MODULE_HANDLE
|
|
// Value2: Url string
|
|
if ((Value1 == NULL) || (Value2 == NULL))
|
|
{
|
|
rc = RC_SET( FERR_INVALID_PARM);
|
|
goto Exit;
|
|
}
|
|
|
|
rc = flmRegisterHttpCallback((FLM_MODULE_HANDLE)Value1, (char *)Value2);
|
|
break;
|
|
|
|
case FLM_DEREGISTER_HTTP_URL:
|
|
rc = flmDeregisterHttpCallback();
|
|
break;
|
|
|
|
case FLM_UNASSIGN_HTTP_SYMS:
|
|
gv_FlmSysData.HttpConfigParms.fnReg = NULL;
|
|
gv_FlmSysData.HttpConfigParms.fnDereg = NULL;
|
|
gv_FlmSysData.HttpConfigParms.fnReqPath = NULL;
|
|
gv_FlmSysData.HttpConfigParms.fnReqQuery = NULL;
|
|
gv_FlmSysData.HttpConfigParms.fnReqHdrValue = NULL;
|
|
gv_FlmSysData.HttpConfigParms.fnSetHdrValue = NULL;
|
|
gv_FlmSysData.HttpConfigParms.fnPrintf = NULL;
|
|
gv_FlmSysData.HttpConfigParms.fnEmit = NULL;
|
|
gv_FlmSysData.HttpConfigParms.fnSetNoCache = NULL;
|
|
gv_FlmSysData.HttpConfigParms.fnSendHeader = NULL;
|
|
gv_FlmSysData.HttpConfigParms.fnSetIOMode = NULL;
|
|
gv_FlmSysData.HttpConfigParms.fnSendBuffer = NULL;
|
|
gv_FlmSysData.HttpConfigParms.fnAcquireSession = NULL;
|
|
gv_FlmSysData.HttpConfigParms.fnReleaseSession = NULL;
|
|
gv_FlmSysData.HttpConfigParms.fnAcquireUser = NULL;
|
|
gv_FlmSysData.HttpConfigParms.fnReleaseUser = NULL;
|
|
gv_FlmSysData.HttpConfigParms.fnSetSessionValue = NULL;
|
|
gv_FlmSysData.HttpConfigParms.fnGetSessionValue = NULL;
|
|
gv_FlmSysData.HttpConfigParms.fnGetGblValue = NULL;
|
|
gv_FlmSysData.HttpConfigParms.fnSetGblValue = NULL;
|
|
gv_FlmSysData.HttpConfigParms.fnRecvBuffer = NULL;
|
|
|
|
break;
|
|
|
|
case FLM_KILL_DB_HANDLES:
|
|
{
|
|
FFILE * pTmpFile;
|
|
|
|
f_mutexLock( gv_FlmSysData.hShareMutex);
|
|
if( Value1)
|
|
{
|
|
// Look up the file using flmFindFile to see if we have the
|
|
// file open. May unlock and re-lock the global mutex.
|
|
|
|
if( RC_OK( flmFindFile( (const char *)Value1,
|
|
(const char *)Value2, &pTmpFile)) && pTmpFile)
|
|
{
|
|
flmSetMustCloseFlags( pTmpFile, FERR_OK, TRUE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( gv_FlmSysData.pFileHashTbl)
|
|
{
|
|
FLMUINT uiLoop;
|
|
|
|
for( uiLoop = 0; uiLoop < FILE_HASH_ENTRIES; uiLoop++)
|
|
{
|
|
pTmpFile =
|
|
(FFILE *)gv_FlmSysData.pFileHashTbl[ uiLoop].pFirstInBucket;
|
|
|
|
while( pTmpFile)
|
|
{
|
|
flmSetMustCloseFlags( pTmpFile, FERR_OK, TRUE);
|
|
pTmpFile = pTmpFile->pNext;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
f_mutexUnlock( gv_FlmSysData.hShareMutex);
|
|
|
|
// Kill all sessions
|
|
|
|
if( gv_FlmSysData.pSessionMgr)
|
|
{
|
|
gv_FlmSysData.pSessionMgr->shutdownSessions();
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case FLM_QUERY_MAX:
|
|
f_mutexLock( gv_FlmSysData.hQueryMutex);
|
|
gv_FlmSysData.uiMaxQueries = (FLMUINT)Value1;
|
|
gv_FlmSysData.bNeedToUnsetMaxQueries = FALSE;
|
|
flmFreeSavedQueries( TRUE);
|
|
|
|
// flmFreeSavedQueries unlocks the mutex.
|
|
|
|
break;
|
|
|
|
case FLM_MAX_DIRTY_CACHE:
|
|
f_mutexLock( gv_FlmSysData.hShareMutex);
|
|
if (!Value1)
|
|
{
|
|
gv_FlmSysData.SCacheMgr.bAutoCalcMaxDirty = TRUE;
|
|
gv_FlmSysData.SCacheMgr.uiMaxDirtyCache = 0;
|
|
gv_FlmSysData.SCacheMgr.uiLowDirtyCache = 0;
|
|
}
|
|
else
|
|
{
|
|
gv_FlmSysData.SCacheMgr.bAutoCalcMaxDirty = FALSE;
|
|
gv_FlmSysData.SCacheMgr.uiMaxDirtyCache = (FLMUINT)Value1;
|
|
|
|
// Low threshhold must be no higher than maximum!
|
|
|
|
if ((gv_FlmSysData.SCacheMgr.uiLowDirtyCache =
|
|
(FLMUINT)Value2) > gv_FlmSysData.SCacheMgr.uiMaxDirtyCache)
|
|
{
|
|
gv_FlmSysData.SCacheMgr.uiLowDirtyCache =
|
|
gv_FlmSysData.SCacheMgr.uiMaxDirtyCache;
|
|
}
|
|
}
|
|
f_mutexUnlock( gv_FlmSysData.hShareMutex);
|
|
break;
|
|
|
|
case FLM_QUERY_STRATIFY_LIMITS:
|
|
f_mutexLock( gv_FlmSysData.hShareMutex);
|
|
gv_FlmSysData.uiMaxStratifyIterations = (FLMUINT)Value1;
|
|
gv_FlmSysData.uiMaxStratifyTime = (FLMUINT)Value2;
|
|
f_mutexUnlock( gv_FlmSysData.hShareMutex);
|
|
break;
|
|
|
|
default:
|
|
rc = RC_SET( FERR_NOT_IMPLEMENTED);
|
|
break;
|
|
}
|
|
|
|
Exit:
|
|
return( rc);
|
|
}
|
|
|
|
/*API~***********************************************************************
|
|
Desc : Gets configured shared attributes.
|
|
*END************************************************************************/
|
|
RCODE FlmGetConfig(
|
|
eFlmConfigTypes eConfigType,
|
|
// [IN] Element to configure. Possible values of eConfigType:
|
|
//
|
|
// PARAM TYPE MEANING / USE
|
|
//
|
|
void * Value1
|
|
// [OUT] Pointer to return value. The type and domain of the
|
|
// value returned are determined by eConfigType.
|
|
)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FLMUINT uiTmp;
|
|
|
|
switch( eConfigType)
|
|
{
|
|
case FLM_CACHE_LIMIT:
|
|
f_mutexLock( gv_FlmSysData.hShareMutex);
|
|
f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex);
|
|
*((FLMUINT *)Value1) = gv_FlmSysData.SCacheMgr.Usage.uiMaxBytes +
|
|
gv_FlmSysData.RCacheMgr.Usage.uiMaxBytes;
|
|
f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex);
|
|
f_mutexUnlock( gv_FlmSysData.hShareMutex);
|
|
break;
|
|
|
|
case FLM_BLOCK_CACHE_PERCENTAGE:
|
|
f_mutexLock( gv_FlmSysData.hShareMutex);
|
|
*((FLMUINT *)Value1) = gv_FlmSysData.uiBlockCachePercentage;
|
|
f_mutexUnlock( gv_FlmSysData.hShareMutex);
|
|
break;
|
|
|
|
case FLM_SCACHE_DEBUG:
|
|
#ifdef FLM_DEBUG
|
|
*((FLMBOOL *)Value1) = gv_FlmSysData.SCacheMgr.bDebug;
|
|
#else
|
|
*((FLMBOOL *)Value1) = FALSE;
|
|
#endif
|
|
break;
|
|
|
|
case FLM_OPEN_FILES:
|
|
*((FLMUINT *)Value1) = gv_FlmSysData.pFileHdlMgr->GetOpenedFiles();
|
|
break;
|
|
|
|
case FLM_OPEN_THRESHOLD:
|
|
*((FLMUINT *)Value1) = gv_FlmSysData.pFileHdlMgr->GetOpenThreshold();
|
|
break;
|
|
|
|
case FLM_TMPDIR:
|
|
f_mutexLock( gv_FlmSysData.hShareMutex);
|
|
|
|
if( !gv_FlmSysData.bTempDirSet )
|
|
{
|
|
rc = RC_SET( FERR_IO_PATH_NOT_FOUND );
|
|
|
|
// Set the output to nulls on failure.
|
|
|
|
*((char *)Value1) = 0;
|
|
}
|
|
else
|
|
{
|
|
f_strcpy( (char *)Value1, gv_FlmSysData.szTempDir);
|
|
}
|
|
|
|
f_mutexUnlock( gv_FlmSysData.hShareMutex);
|
|
break;
|
|
|
|
case FLM_MAX_CP_INTERVAL:
|
|
FLM_TIMER_UNITS_TO_SECS( gv_FlmSysData.uiMaxCPInterval, uiTmp);
|
|
*((FLMUINT *)Value1) = uiTmp;
|
|
break;
|
|
|
|
case FLM_BLOB_EXT:
|
|
f_strcpy( (char *)Value1, (const char *)gv_FlmSysData.ucBlobExt);
|
|
break;
|
|
|
|
case FLM_MAX_TRANS_SECS:
|
|
FLM_TIMER_UNITS_TO_SECS( gv_FlmSysData.uiMaxTransTime, uiTmp);
|
|
*((FLMUINT *)Value1) = uiTmp;
|
|
break;
|
|
|
|
case FLM_MAX_TRANS_INACTIVE_SECS:
|
|
FLM_TIMER_UNITS_TO_SECS( gv_FlmSysData.uiMaxTransInactiveTime, uiTmp);
|
|
*((FLMUINT *)Value1) = (FLMUINT)uiTmp;
|
|
break;
|
|
|
|
case FLM_CACHE_ADJUST_INTERVAL:
|
|
FLM_TIMER_UNITS_TO_SECS( gv_FlmSysData.uiCacheAdjustInterval, uiTmp);
|
|
*((FLMUINT *)Value1) = uiTmp;
|
|
break;
|
|
|
|
case FLM_CACHE_CLEANUP_INTERVAL:
|
|
FLM_TIMER_UNITS_TO_SECS( gv_FlmSysData.uiCacheCleanupInterval, uiTmp);
|
|
*((FLMUINT *)Value1) = uiTmp;
|
|
break;
|
|
|
|
case FLM_UNUSED_CLEANUP_INTERVAL:
|
|
FLM_TIMER_UNITS_TO_SECS( gv_FlmSysData.uiUnusedCleanupInterval, uiTmp);
|
|
*((FLMUINT *)Value1) = uiTmp;
|
|
break;
|
|
|
|
case FLM_MAX_UNUSED_TIME:
|
|
f_mutexLock( gv_FlmSysData.hShareMutex);
|
|
FLM_TIMER_UNITS_TO_SECS( gv_FlmSysData.uiMaxUnusedTime, uiTmp);
|
|
f_mutexUnlock( gv_FlmSysData.hShareMutex);
|
|
*((FLMUINT *)Value1) = uiTmp;
|
|
break;
|
|
|
|
case FLM_OUT_OF_MEM_SIMULATION:
|
|
#ifdef DEBUG_SIM_OUT_OF_MEM
|
|
*((FLMBOOL *)Value1) =
|
|
(gv_FlmSysData.uiOutOfMemSimEnabledFlag ==
|
|
OUT_OF_MEM_SIM_ENABLED_FLAG)
|
|
? TRUE
|
|
: FALSE;
|
|
#else
|
|
*((FLMBOOL *)Value1) = FALSE;
|
|
#endif
|
|
break;
|
|
|
|
case FLM_CACHE_CHECK:
|
|
*((FLMBOOL *)Value1) = gv_FlmSysData.bCheckCache;
|
|
break;
|
|
|
|
case FLM_USE_ESM:
|
|
*((FLMBOOL *)Value1) = gv_FlmSysData.bOkToUseESM;
|
|
break;
|
|
|
|
case FLM_QUERY_MAX:
|
|
f_mutexLock( gv_FlmSysData.hQueryMutex);
|
|
*((FLMUINT *)Value1) = gv_FlmSysData.uiMaxQueries;
|
|
f_mutexUnlock( gv_FlmSysData.hQueryMutex);
|
|
break;
|
|
|
|
case FLM_MAX_DIRTY_CACHE:
|
|
f_mutexLock( gv_FlmSysData.hShareMutex);
|
|
*((FLMUINT *)Value1) = gv_FlmSysData.SCacheMgr.uiMaxDirtyCache;
|
|
f_mutexUnlock( gv_FlmSysData.hShareMutex);
|
|
break;
|
|
|
|
case FLM_DYNA_CACHE_SUPPORTED:
|
|
#ifdef FLM_CAN_GET_PHYS_MEM
|
|
*((FLMBOOL *)Value1) = TRUE;
|
|
#else
|
|
*((FLMBOOL *)Value1) = FALSE;
|
|
#endif
|
|
break;
|
|
|
|
case FLM_QUERY_STRATIFY_LIMITS:
|
|
f_mutexLock( gv_FlmSysData.hShareMutex);
|
|
if (Value1)
|
|
{
|
|
*((FLMUINT *)Value1) = gv_FlmSysData.uiMaxStratifyIterations;
|
|
}
|
|
f_mutexUnlock( gv_FlmSysData.hShareMutex);
|
|
break;
|
|
|
|
default:
|
|
rc = RC_SET( FERR_NOT_IMPLEMENTED);
|
|
break;
|
|
}
|
|
|
|
//Exit:
|
|
return( rc);
|
|
}
|
|
|
|
/*API~***********************************************************************
|
|
Desc:
|
|
*END************************************************************************/
|
|
RCODE FlmGetThreadInfo(
|
|
POOL * pPool,
|
|
F_THREAD_INFO ** ppThreadInfo,
|
|
FLMUINT * puiNumThreads,
|
|
const char * pszUrl)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
CS_CONTEXT_p pCSContext = NULL;
|
|
|
|
if( pszUrl)
|
|
{
|
|
// flmGetCSConnection may return a NULL CS_CONTEXT if the
|
|
// URL references a local resource.
|
|
|
|
if( RC_BAD( rc = flmGetCSConnection( pszUrl, &pCSContext)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if( pCSContext)
|
|
{
|
|
NODE * pTree;
|
|
FCL_WIRE Wire;
|
|
|
|
Wire.setContext( pCSContext);
|
|
|
|
// Send a request get statistics
|
|
|
|
if (RC_BAD( Wire.sendOp( FCS_OPCLASS_GLOBAL,
|
|
FCS_OP_GLOBAL_GET_THREAD_INFO)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (RC_BAD( Wire.sendTerminate()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Read the response.
|
|
|
|
if (RC_BAD( Wire.read()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (RC_BAD( Wire.getRCode()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( Wire.getHTD( pPool, &pTree)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( rc = fcsExtractThreadInfo( pTree, pPool,
|
|
ppThreadInfo, puiNumThreads)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( RC_BAD( rc = gv_FlmSysData.pThreadMgr->getThreadInfo( pPool,
|
|
ppThreadInfo, puiNumThreads)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
if( pCSContext)
|
|
{
|
|
flmCloseCSConnection( &pCSContext);
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Returns the temporary directory (path) that is part of the share
|
|
structure. We have to pass in the FDB structure in order
|
|
to know if we should lock a mutex in order to access the
|
|
path structure.
|
|
****************************************************************************/
|
|
RCODE flmGetTmpDir(
|
|
char * pszOutputTmpDir)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
|
|
f_mutexLock( gv_FlmSysData.hShareMutex);
|
|
|
|
// Return an error if the temp directory has not been set.
|
|
|
|
if( !gv_FlmSysData.bTempDirSet)
|
|
{
|
|
rc = RC_SET( FERR_IO_PATH_NOT_FOUND );
|
|
|
|
// Set the output to nulls on failure.
|
|
*pszOutputTmpDir = 0;
|
|
}
|
|
else
|
|
{
|
|
f_strcpy( pszOutputTmpDir, gv_FlmSysData.szTempDir);
|
|
}
|
|
|
|
f_mutexUnlock( gv_FlmSysData.hShareMutex);
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This shuts down the background threads
|
|
Note: This routine assumes that the global mutex is locked. The mutex will
|
|
be unlocked internally, but will always be locked on exit.
|
|
****************************************************************************/
|
|
FSTATIC void flmShutdownDbThreads(
|
|
FFILE * pFile)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
F_BKGND_IX * pBackgroundIx;
|
|
FDB * pDb;
|
|
F_Thread * pThread;
|
|
FLMUINT uiThreadId;
|
|
FLMUINT uiThreadCount;
|
|
FLMBOOL bMutexLocked = TRUE;
|
|
|
|
// Shut down the tracker thread
|
|
|
|
if( pFile->pMaintThrd)
|
|
{
|
|
pFile->pMaintThrd->setShutdownFlag();
|
|
f_semSignal( pFile->hMaintSem);
|
|
|
|
// Unlock the global mutex
|
|
|
|
f_mutexUnlock( gv_FlmSysData.hShareMutex);
|
|
bMutexLocked = FALSE;
|
|
|
|
pFile->pMaintThrd->stopThread();
|
|
|
|
// Re-lock the mutex
|
|
|
|
f_mutexLock( gv_FlmSysData.hShareMutex);
|
|
bMutexLocked = TRUE;
|
|
|
|
pFile->pMaintThrd->Release();
|
|
pFile->pMaintThrd = NULL;
|
|
|
|
f_semDestroy( &pFile->hMaintSem);
|
|
}
|
|
|
|
// Signal all background threads to shut down that are
|
|
// associated with this FFILE
|
|
|
|
for( ;;)
|
|
{
|
|
uiThreadCount = 0;
|
|
|
|
// Shut down all background indexing threads.
|
|
|
|
uiThreadId = 0;
|
|
for( ;;)
|
|
{
|
|
if( RC_BAD( rc = gv_FlmSysData.pThreadMgr->getNextGroupThread(
|
|
&pThread, FLM_BACKGROUND_INDEXING_THREAD_GROUP, &uiThreadId)))
|
|
{
|
|
if( rc == FERR_NOT_FOUND)
|
|
{
|
|
rc = FERR_OK;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
flmAssert( 0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pBackgroundIx = (F_BKGND_IX *)pThread->getParm1();
|
|
if( pBackgroundIx && pBackgroundIx->pFile == pFile)
|
|
{
|
|
// Set the thread's terminate flag.
|
|
|
|
uiThreadCount++;
|
|
pThread->setShutdownFlag();
|
|
}
|
|
|
|
pThread->Release();
|
|
pThread = NULL;
|
|
}
|
|
}
|
|
|
|
// Shut down all threads in the FLM_DB_THREAD_GROUP.
|
|
|
|
uiThreadId = 0;
|
|
for( ;;)
|
|
{
|
|
if( RC_BAD( rc = gv_FlmSysData.pThreadMgr->getNextGroupThread(
|
|
&pThread, FLM_DB_THREAD_GROUP, &uiThreadId)))
|
|
{
|
|
if( rc == FERR_NOT_FOUND)
|
|
{
|
|
rc = FERR_OK;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
flmAssert( 0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pDb = (FDB *)pThread->getParm2();
|
|
if (pDb && pDb->pFile == pFile)
|
|
{
|
|
|
|
// Set the thread's terminate flag.
|
|
|
|
uiThreadCount++;
|
|
pThread->setShutdownFlag();
|
|
}
|
|
|
|
pThread->Release();
|
|
pThread = NULL;
|
|
}
|
|
}
|
|
|
|
if( !uiThreadCount)
|
|
{
|
|
break;
|
|
}
|
|
|
|
// Unlock the global mutex
|
|
|
|
f_mutexUnlock( gv_FlmSysData.hShareMutex);
|
|
bMutexLocked = FALSE;
|
|
|
|
// Give the threads a chance to terminate
|
|
|
|
f_sleep( 50);
|
|
|
|
// Re-lock the mutex and see if any threads are still active
|
|
|
|
f_mutexLock( gv_FlmSysData.hShareMutex);
|
|
bMutexLocked = TRUE;
|
|
}
|
|
|
|
// Re-lock the mutex
|
|
|
|
if( !bMutexLocked)
|
|
{
|
|
f_mutexLock( gv_FlmSysData.hShareMutex);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine frees all of the structures associated with a file.
|
|
Whoever called this routine has already determined that it is safe
|
|
to do so.
|
|
Notes: The global mutex is assumed to be locked when entering the
|
|
routine. It may be unlocked and re-locked before the routine
|
|
exits, however.
|
|
****************************************************************************/
|
|
void flmFreeFile(
|
|
FFILE * pFile) // File to be freed.
|
|
{
|
|
FNOTIFY * pCloseNotifies;
|
|
|
|
// See if another thread is in the process of closing
|
|
// this FFILE. It is possible for this to happen, since
|
|
// the monitor thread may have selected this FFILE to be
|
|
// closed because it has been unused for a period of time.
|
|
// At the same time, a foreground thread could have called
|
|
// FlmConfig to close all unused FFILEs. Since flmFreeFile
|
|
// may unlock and re-lock the mutex, there is a small window
|
|
// of opportunity for both threads to try to free the same
|
|
// FFILE.
|
|
|
|
if( pFile->uiFlags & DBF_BEING_CLOSED)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Set the DBF_BEING_CLOSED flag
|
|
|
|
pFile->uiFlags |= DBF_BEING_CLOSED;
|
|
|
|
// Shut down all background threads before shutting down the CP thread.
|
|
|
|
flmShutdownDbThreads( pFile);
|
|
|
|
// At this point, the use count better be zero
|
|
|
|
flmAssert( pFile->uiUseCount == 0);
|
|
|
|
// Unlock the mutex
|
|
|
|
f_mutexUnlock( gv_FlmSysData.hShareMutex);
|
|
|
|
// Shutdown the checkpoint thread
|
|
|
|
if( pFile->pCPThrd)
|
|
{
|
|
pFile->pCPThrd->stopThread();
|
|
pFile->pCPThrd->Release();
|
|
pFile->pCPThrd = NULL;
|
|
}
|
|
|
|
f_mutexLock( gv_FlmSysData.hShareMutex);
|
|
|
|
// Unlink all of the DICTs that are connected to the file.
|
|
|
|
while( pFile->pDictList)
|
|
{
|
|
flmUnlinkDict( pFile->pDictList);
|
|
}
|
|
|
|
// Take the file out of its name hash bucket, if any.
|
|
|
|
if (pFile->uiBucket != 0xFFFF)
|
|
{
|
|
flmUnlinkFileFromBucket( pFile);
|
|
}
|
|
|
|
// Unlink the file from the not-used list
|
|
|
|
flmUnlinkFileFromNUList( pFile);
|
|
|
|
// Free the RFL data, if any.
|
|
|
|
if (pFile->pRfl)
|
|
{
|
|
pFile->pRfl->Release();
|
|
pFile->pRfl = NULL;
|
|
}
|
|
|
|
// Free any open notify requests associated with the file.
|
|
// We shouldn't have any open notifies at this point, but
|
|
// we'll be nice and clean up any resources anyway.
|
|
|
|
flmAssert( pFile->pOpenNotifies == NULL);
|
|
flmFreeNotifyList( &pFile->pOpenNotifies);
|
|
|
|
// Save pCloseNotifies -- we will notify any waiters once the
|
|
// FFILE has been freed.
|
|
|
|
pCloseNotifies = pFile->pCloseNotifies;
|
|
|
|
// Free any dictionary usage structures associated with the file.
|
|
|
|
flmFreeDictList( &pFile->pDictList);
|
|
|
|
// Free any shared cache associated with the file.
|
|
|
|
ScaFreeFileCache( pFile);
|
|
|
|
// Free any record cache associated with the file.
|
|
|
|
flmRcaFreeFileRecs( pFile);
|
|
|
|
// Free the file ID list. This will also remove all file handles
|
|
// associated with this file.
|
|
|
|
if( pFile->pFileIdList)
|
|
{
|
|
FLMUINT uiRefCnt;
|
|
|
|
uiRefCnt = pFile->pFileIdList->Release();
|
|
flmAssert( !uiRefCnt);
|
|
pFile->pFileIdList = NULL;
|
|
}
|
|
|
|
// Release the lock objects.
|
|
|
|
if( pFile->pWriteLockObj)
|
|
{
|
|
pFile->pWriteLockObj->Release();
|
|
pFile->pWriteLockObj = NULL;
|
|
}
|
|
|
|
if( pFile->pFileLockObj)
|
|
{
|
|
pFile->pFileLockObj->Release();
|
|
pFile->pFileLockObj = NULL;
|
|
}
|
|
|
|
// Close and delete the lock file.
|
|
|
|
if( pFile->pLockFileHdl)
|
|
{
|
|
(void)pFile->pLockFileHdl->Close();
|
|
pFile->pLockFileHdl->Release();
|
|
pFile->pLockFileHdl = NULL;
|
|
}
|
|
|
|
// Free the write buffer managers.
|
|
|
|
if( pFile->pBufferMgr)
|
|
{
|
|
pFile->pBufferMgr->Release();
|
|
pFile->pBufferMgr = NULL;
|
|
}
|
|
|
|
// Free the log header write buffer
|
|
|
|
if( pFile->pucLogHdrWriteBuf)
|
|
{
|
|
#ifdef FLM_WIN
|
|
(void)VirtualFree( pFile->pucLogHdrWriteBuf, 0, MEM_RELEASE);
|
|
pFile->pucLogHdrWriteBuf = NULL;
|
|
#elif defined( FLM_LINUX) || defined( FLM_SOLARIS)
|
|
free( pFile->pucLogHdrWriteBuf);
|
|
pFile->pucLogHdrWriteBuf = NULL;
|
|
#else
|
|
f_free( &pFile->pucLogHdrWriteBuf);
|
|
#endif
|
|
}
|
|
|
|
GedPoolFree( &pFile->krefPool);
|
|
|
|
if( pFile->ppBlocksDone)
|
|
{
|
|
f_free( &pFile->ppBlocksDone);
|
|
pFile->uiBlocksDoneArraySize = 0;
|
|
}
|
|
|
|
if( pFile->pECacheMgr)
|
|
{
|
|
pFile->pECacheMgr->Release();
|
|
pFile->pECacheMgr = NULL;
|
|
}
|
|
|
|
// Free the maintenance thread's semaphore
|
|
|
|
if( pFile->hMaintSem != F_SEM_NULL)
|
|
{
|
|
f_semDestroy( &pFile->hMaintSem);
|
|
}
|
|
|
|
// Free the database wrapping key
|
|
|
|
if( pFile->pDbWrappingKey)
|
|
{
|
|
pFile->pDbWrappingKey->Release();
|
|
pFile->pDbWrappingKey = NULL;
|
|
}
|
|
|
|
// Free the password
|
|
|
|
if ( pFile->pszDbPassword)
|
|
{
|
|
f_free( &pFile->pszDbPassword);
|
|
}
|
|
|
|
// Free the FFILE
|
|
|
|
f_free( &pFile);
|
|
|
|
// Notify waiters that the FFILE is gone
|
|
|
|
while( pCloseNotifies)
|
|
{
|
|
F_SEM hSem;
|
|
|
|
*(pCloseNotifies->pRc) = FERR_OK;
|
|
hSem = pCloseNotifies->hSem;
|
|
pCloseNotifies = pCloseNotifies->pNext;
|
|
f_semSignal( hSem);
|
|
}
|
|
|
|
// Global mutex is still locked at this point
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine frees a registered event.
|
|
****************************************************************************/
|
|
FSTATIC void flmFreeEvent(
|
|
FEVENT * pEvent,
|
|
F_MUTEX hMutex,
|
|
FEVENT ** ppEventListRV
|
|
)
|
|
{
|
|
f_mutexLock( hMutex);
|
|
if (pEvent->pPrev)
|
|
{
|
|
pEvent->pPrev->pNext = pEvent->pNext;
|
|
}
|
|
else
|
|
{
|
|
*ppEventListRV = pEvent->pNext;
|
|
}
|
|
if (pEvent->pNext)
|
|
{
|
|
pEvent->pNext->pPrev = pEvent->pPrev;
|
|
}
|
|
f_mutexUnlock( hMutex);
|
|
f_free( &pEvent);
|
|
}
|
|
|
|
/************************************************************************
|
|
Desc : Cleans up - assumes that the spin lock has already been
|
|
obtained. This allows it to be called directly from
|
|
FlmStartup on error conditions.
|
|
************************************************************************/
|
|
FSTATIC void flmCleanup( void)
|
|
{
|
|
FLMUINT uiCnt;
|
|
FLMINT iEventCategory;
|
|
|
|
// NOTE: We are checking and decrementing a global variable here.
|
|
// However, on platforms that properly support atomic exchange,
|
|
// we are OK, because the caller has obtained a spin lock before
|
|
// calling this routine, so we are guaranteed to be the only thread
|
|
// executing this code at this point. On platforms that don't
|
|
// support atomic exchange, our spin lock will be less reliable for
|
|
// really tight race conditions. But in reality, nobody should be
|
|
// calling FlmStartup and FlmShutdown in race conditions like that
|
|
// anyway. We are only doing the spin lock stuff to try and be
|
|
// nice about it if they are.
|
|
|
|
// This check allows FlmShutdown to be called before calling
|
|
// FlmStartup, or even if FlmStartup fails.
|
|
|
|
if (!gv_uiFlmSysStartupCount)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// If we decrement the count and it doesn't go to zero, we are not
|
|
// ready to do cleanup yet.
|
|
|
|
if (--gv_uiFlmSysStartupCount > 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Deregister the Http Callback function if it has been registered.
|
|
// Note: Usually, this is all handled at the SMI level (in
|
|
// SMDIBHandle::exit). This code is here for the cases where we're
|
|
// part of Flint or some other utility that doesn't necessarily use
|
|
// SMI...
|
|
if (gv_FlmSysData.HttpConfigParms.fnDereg)
|
|
{
|
|
FlmConfig( FLM_DEREGISTER_HTTP_URL, NULL, NULL);
|
|
}
|
|
|
|
// Free any queries that have been saved in the query list.
|
|
|
|
if (gv_FlmSysData.hQueryMutex != F_MUTEX_NULL)
|
|
{
|
|
|
|
// Setting uiMaxQueries to zero will cause flmFreeSavedQueries
|
|
// to free the entire list. Also, embedded queries will not be
|
|
// added back into the list when uiMaxQueries is zero.
|
|
|
|
gv_FlmSysData.uiMaxQueries = 0;
|
|
flmFreeSavedQueries( FALSE);
|
|
}
|
|
|
|
// Shut down the monitor thread, if there is one.
|
|
|
|
f_threadDestroy( &gv_FlmSysData.pMonitorThrd);
|
|
|
|
// Shut down the session manager
|
|
|
|
if( gv_FlmSysData.pSessionMgr)
|
|
{
|
|
gv_FlmSysData.pSessionMgr->Release();
|
|
gv_FlmSysData.pSessionMgr = NULL;
|
|
}
|
|
|
|
// Destroy the session mutex
|
|
|
|
if( gv_FlmSysData.hHttpSessionMutex != F_MUTEX_NULL)
|
|
{
|
|
f_mutexDestroy( &gv_FlmSysData.hHttpSessionMutex);
|
|
}
|
|
|
|
gv_FlmSysData.pMrnuFile = NULL;
|
|
gv_FlmSysData.pLrnuFile = NULL;
|
|
|
|
/* Free all of the files and associated structures. */
|
|
|
|
if (gv_FlmSysData.pFileHashTbl)
|
|
{
|
|
FBUCKET * pFileHashTbl;
|
|
|
|
// flmFreeFile expects the global mutex to be locked
|
|
// IMPORTANT NOTE: pFileHashTbl is ALWAYS allocated
|
|
// AFTER the mutex is allocated, so we are guaranteed
|
|
// to have a mutex if pFileHashTbl is non-NULL.
|
|
|
|
f_mutexLock( gv_FlmSysData.hShareMutex);
|
|
for (uiCnt = 0, pFileHashTbl = gv_FlmSysData.pFileHashTbl;
|
|
uiCnt < FILE_HASH_ENTRIES;
|
|
uiCnt++, pFileHashTbl++)
|
|
{
|
|
FFILE * pFile = (FFILE *)pFileHashTbl->pFirstInBucket;
|
|
FFILE * pTmpFile;
|
|
|
|
while( pFile)
|
|
{
|
|
pTmpFile = pFile;
|
|
pFile = pFile->pNext;
|
|
flmFreeFile( pTmpFile);
|
|
}
|
|
pFileHashTbl->pFirstInBucket = NULL;
|
|
}
|
|
|
|
// Unlock the global mutex
|
|
f_mutexUnlock( gv_FlmSysData.hShareMutex);
|
|
|
|
// Free the hash table
|
|
f_free( &gv_FlmSysData.pFileHashTbl);
|
|
}
|
|
|
|
// Free the statistics.
|
|
|
|
if (gv_FlmSysData.bStatsInitialized)
|
|
{
|
|
FlmFreeStats( &gv_FlmSysData.Stats);
|
|
gv_FlmSysData.bStatsInitialized = FALSE;
|
|
}
|
|
|
|
// Free (release) FLAIM's File Shared File Handles.
|
|
|
|
if (gv_FlmSysData.pFileHdlMgr)
|
|
{
|
|
FLMUINT uiRefCnt = gv_FlmSysData.pFileHdlMgr->Release();
|
|
|
|
// No one else should have a reference to the file handle manager
|
|
// after this point.
|
|
|
|
#ifdef FLM_DEBUG
|
|
flmAssert( 0 == uiRefCnt);
|
|
#else
|
|
// Quiet the compiler about the unused variable
|
|
(void)uiRefCnt;
|
|
#endif
|
|
gv_FlmSysData.pFileHdlMgr = NULL;
|
|
}
|
|
|
|
// Free (release) FLAIM's Server Lock Manager.
|
|
|
|
if (gv_FlmSysData.pServerLockMgr)
|
|
{
|
|
FLMUINT uiRefCnt;
|
|
|
|
// Release all locks.
|
|
|
|
gv_FlmSysData.pServerLockMgr->CheckLockTimeouts( TRUE);
|
|
|
|
// Release the lock manager.
|
|
|
|
uiRefCnt = gv_FlmSysData.pServerLockMgr->Release();
|
|
|
|
// No one else should have a reference to the server lock manager
|
|
// at this point, so lets trip a flmAssert if the object was really
|
|
// not deleted.
|
|
|
|
#ifdef FLM_DEBUG
|
|
flmAssert( 0 == uiRefCnt);
|
|
#else
|
|
// Quiet the compiler about the unused variable
|
|
(void)uiRefCnt;
|
|
#endif
|
|
gv_FlmSysData.pServerLockMgr = NULL;
|
|
}
|
|
|
|
// Free the resources of the shared cache manager.
|
|
|
|
ScaExit();
|
|
|
|
// Free the resources of the record cache manager.
|
|
|
|
flmRcaExit();
|
|
|
|
// Free the mutexes last of all.
|
|
|
|
if (gv_FlmSysData.hQueryMutex != F_MUTEX_NULL)
|
|
{
|
|
f_mutexDestroy( &gv_FlmSysData.hQueryMutex);
|
|
}
|
|
|
|
if (gv_FlmSysData.hShareMutex != F_MUTEX_NULL)
|
|
{
|
|
f_mutexDestroy( &gv_FlmSysData.hShareMutex);
|
|
}
|
|
|
|
if (gv_FlmSysData.hFileHdlMutex != F_MUTEX_NULL)
|
|
{
|
|
f_mutexDestroy( &gv_FlmSysData.hFileHdlMutex);
|
|
}
|
|
|
|
if (gv_FlmSysData.hServerLockMgrMutex != F_MUTEX_NULL)
|
|
{
|
|
f_mutexDestroy( &gv_FlmSysData.hServerLockMgrMutex);
|
|
}
|
|
|
|
if (gv_FlmSysData.HttpConfigParms.hMutex != F_MUTEX_NULL)
|
|
{
|
|
f_mutexDestroy( &gv_FlmSysData.HttpConfigParms.hMutex);
|
|
}
|
|
|
|
|
|
// Free up callbacks that have been registered for events.
|
|
|
|
for (iEventCategory = 0;
|
|
iEventCategory < F_MAX_EVENT_CATEGORIES;
|
|
iEventCategory++)
|
|
{
|
|
if (gv_FlmSysData.EventHdrs [iEventCategory].hMutex != F_MUTEX_NULL)
|
|
{
|
|
while (gv_FlmSysData.EventHdrs [iEventCategory].pEventCBList)
|
|
{
|
|
flmFreeEvent(
|
|
gv_FlmSysData.EventHdrs [iEventCategory].pEventCBList,
|
|
gv_FlmSysData.EventHdrs [iEventCategory].hMutex,
|
|
&gv_FlmSysData.EventHdrs [iEventCategory].pEventCBList);
|
|
}
|
|
f_mutexDestroy( &gv_FlmSysData.EventHdrs [iEventCategory].hMutex);
|
|
}
|
|
}
|
|
|
|
// Free (release) FLAIM's File Shared File System Object.
|
|
|
|
if (gv_FlmSysData.pFileSystem)
|
|
{
|
|
FLMUINT uiRefCnt = gv_FlmSysData.pFileSystem->Release();
|
|
|
|
// No one else should have a reference to the file system
|
|
// after this point.
|
|
|
|
#ifdef FLM_DEBUG
|
|
flmAssert( 0 == uiRefCnt);
|
|
#else
|
|
// Quiet the compiler about the unused variable
|
|
(void)uiRefCnt;
|
|
#endif
|
|
gv_FlmSysData.pFileSystem = NULL;
|
|
}
|
|
#ifdef FLM_DBG_LOG
|
|
flmDbgLogExit();
|
|
#endif
|
|
|
|
// Free the serial number generator
|
|
|
|
f_freeSerialNumberGenerator();
|
|
|
|
#if defined( FLM_NLM)
|
|
nssUninitialize();
|
|
#endif
|
|
|
|
// Release the logger (if any)
|
|
|
|
if( gv_FlmSysData.pLogger)
|
|
{
|
|
gv_FlmSysData.pLogger->lockLogger();
|
|
if( gv_FlmSysData.pLogger->Release() >= 1)
|
|
{
|
|
gv_FlmSysData.pLogger->unlockLogger();
|
|
}
|
|
gv_FlmSysData.pLogger = NULL;
|
|
}
|
|
|
|
// Release the thread manager
|
|
|
|
if( gv_FlmSysData.pThreadMgr)
|
|
{
|
|
gv_FlmSysData.pThreadMgr->Release();
|
|
gv_FlmSysData.pThreadMgr = NULL;
|
|
}
|
|
|
|
// Release the slab manager
|
|
|
|
if( gv_FlmSysData.pSlabManager)
|
|
{
|
|
gv_FlmSysData.pSlabManager->Release();
|
|
gv_FlmSysData.pSlabManager = NULL;
|
|
}
|
|
|
|
// Shutdown NICI
|
|
|
|
#ifdef FLM_USE_NICI
|
|
CCS_Shutdown();
|
|
#endif
|
|
|
|
// Memory cleanup needs to be last.
|
|
|
|
f_memoryCleanup();
|
|
|
|
#ifdef FLM_NLM
|
|
if( gv_bNetWareStartupCalled)
|
|
{
|
|
f_netwareShutdown();
|
|
gv_bNetWareStartupCalled = FALSE;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*API~***********************************************************************
|
|
Desc : Shuts down FLAIM.
|
|
Notes: Allows itself to be called multiple times and even before FlmStartup
|
|
is called, or even if FlmStartup fails. Warning: May not handle
|
|
race conditions very well on platforms that do not support atomic
|
|
exchange.
|
|
*END************************************************************************/
|
|
void FlmShutdown( void)
|
|
{
|
|
flmLockSysData();
|
|
flmCleanup();
|
|
flmUnlockSysData();
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine determines the hash bucket for a string.
|
|
****************************************************************************/
|
|
FLMUINT flmStrHashBucket(
|
|
const char * pszStr,
|
|
FBUCKET * pHashTbl,
|
|
FLMUINT uiNumBuckets)
|
|
{
|
|
FLMUINT uiHashIndex;
|
|
|
|
if ((uiHashIndex = (FLMUINT)*pszStr) >= uiNumBuckets)
|
|
{
|
|
uiHashIndex -= uiNumBuckets;
|
|
}
|
|
|
|
while( *pszStr)
|
|
{
|
|
if ((uiHashIndex =
|
|
(FLMUINT)((pHashTbl [uiHashIndex].uiHashValue) ^ (FLMUINT)(*pszStr))) >=
|
|
uiNumBuckets)
|
|
uiHashIndex -= uiNumBuckets;
|
|
pszStr++;
|
|
}
|
|
return( uiHashIndex);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine determines the hash bucket for a binary array of
|
|
characters.
|
|
****************************************************************************/
|
|
FLMUINT flmBinHashBucket(
|
|
void * pBuf,
|
|
FLMUINT uiBufLen,
|
|
FBUCKET * pHashTbl,
|
|
FLMUINT uiNumBuckets)
|
|
{
|
|
FLMUINT uiHashIndex;
|
|
FLMBYTE * ptr = (FLMBYTE *)pBuf;
|
|
|
|
if ((uiHashIndex = (FLMUINT)*ptr) >= uiNumBuckets)
|
|
uiHashIndex -= uiNumBuckets;
|
|
while (uiBufLen)
|
|
{
|
|
if ((uiHashIndex =
|
|
(FLMUINT)((pHashTbl [uiHashIndex].uiHashValue) ^ (FLMUINT)(*ptr))) >=
|
|
uiNumBuckets)
|
|
uiHashIndex -= uiNumBuckets;
|
|
ptr++;
|
|
uiBufLen--;
|
|
}
|
|
return( uiHashIndex);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine links a notify request into a notification list and
|
|
then waits to be notified that the event has occurred.
|
|
Notes:
|
|
This routine assumes that the shared mutex is locked and that
|
|
it is supposed to unlock it.
|
|
****************************************************************************/
|
|
RCODE flmWaitNotifyReq(
|
|
F_MUTEX hMutex,
|
|
FNOTIFY ** ppNotifyListRV, /* Pointer to the head of a notify
|
|
list where the new notify
|
|
request should be linked into. */
|
|
void * UserData /* Other user data that the notifier
|
|
can use to pass other information
|
|
to the waiter. */
|
|
)
|
|
{
|
|
FNOTIFY * pNotify = NULL;
|
|
RCODE TempRc;
|
|
RCODE rc = FERR_OK;
|
|
F_SEM hSem;
|
|
|
|
/* First create a notify request and link it into the list. */
|
|
|
|
if (RC_OK( rc = f_calloc( (FLMUINT)(sizeof( FNOTIFY)), &pNotify)))
|
|
{
|
|
|
|
/* Allocate a semaphore for the notify request. */
|
|
|
|
pNotify->uiThreadId = f_threadId();
|
|
pNotify->hSem = F_SEM_NULL;
|
|
rc = f_semCreate( &pNotify->hSem);
|
|
}
|
|
if (RC_BAD( rc))
|
|
{
|
|
if (pNotify)
|
|
{
|
|
if (pNotify->hSem != F_SEM_NULL)
|
|
{
|
|
f_semDestroy( &pNotify->hSem);
|
|
}
|
|
f_free( &pNotify);
|
|
}
|
|
goto Exit;
|
|
}
|
|
pNotify->pRc = &rc;
|
|
pNotify->UserData = UserData;
|
|
pNotify->pNext = *ppNotifyListRV;
|
|
*ppNotifyListRV = pNotify;
|
|
hSem = pNotify->hSem;
|
|
|
|
/* Unlock the mutex and wait on the semaphore. */
|
|
|
|
f_mutexUnlock( hMutex);
|
|
if (RC_BAD( TempRc = f_semWait( hSem, F_SEM_WAITFOREVER)))
|
|
{
|
|
rc = TempRc;
|
|
}
|
|
|
|
/* Free the semaphore and the notify structure. */
|
|
|
|
f_semDestroy( &hSem);
|
|
f_free( &pNotify);
|
|
|
|
// Relock the mutex
|
|
|
|
f_mutexLock( hMutex);
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine links an FFILE structure to its name hash bucket.
|
|
NOTE: This function assumes that the global mutex has been
|
|
locked.
|
|
****************************************************************************/
|
|
RCODE flmLinkFileToBucket(
|
|
FFILE * pFile /* File to be linked to its name hash bucket. */
|
|
)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FFILE * pTmpFile;
|
|
FBUCKET * pBucket;
|
|
FLMUINT uiBucket;
|
|
char szDbPathStr [F_PATH_MAX_SIZE];
|
|
|
|
pBucket = gv_FlmSysData.pFileHashTbl;
|
|
|
|
// Normalize the path to a string before hashing on it.
|
|
|
|
if (RC_BAD( rc = f_pathToStorageString( pFile->pszDbPath, szDbPathStr)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
uiBucket = flmStrHashBucket( szDbPathStr, pBucket, FILE_HASH_ENTRIES);
|
|
pBucket = &pBucket [uiBucket];
|
|
if (pBucket->pFirstInBucket)
|
|
{
|
|
pTmpFile = (FFILE *)pBucket->pFirstInBucket;
|
|
pTmpFile->pPrev = pFile;
|
|
}
|
|
pFile->uiBucket = uiBucket;
|
|
pFile->pPrev = (FFILE *)NULL;
|
|
pFile->pNext = (FFILE *)pBucket->pFirstInBucket;
|
|
pBucket->pFirstInBucket = pFile;
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
Desc: This routine unlinks an FFILE structure from its name hash bucket.
|
|
NOTE: This function assumes that the global mutex has been
|
|
locked.
|
|
****************************************************************************/
|
|
FSTATIC void flmUnlinkFileFromBucket(
|
|
FFILE * pFile /* Pointer to file that is to be unlinked from
|
|
its name hash bucket. */
|
|
)
|
|
{
|
|
if (pFile->uiBucket != 0xFFFF)
|
|
{
|
|
if (pFile->pPrev)
|
|
{
|
|
pFile->pPrev->pNext = pFile->pNext;
|
|
}
|
|
else
|
|
{
|
|
gv_FlmSysData.pFileHashTbl [pFile->uiBucket].pFirstInBucket = pFile->pNext;
|
|
}
|
|
if (pFile->pNext)
|
|
{
|
|
pFile->pNext->pPrev = pFile->pPrev;
|
|
}
|
|
pFile->uiBucket = 0xFFFF;
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine links an FFILE structure to the unused list.
|
|
NOTE: This function assumes that the global mutex has been
|
|
locked.
|
|
****************************************************************************/
|
|
void flmLinkFileToNUList(
|
|
FFILE * pFile,
|
|
FLMBOOL bQuickTimeout)
|
|
{
|
|
|
|
if( !bQuickTimeout)
|
|
{
|
|
pFile->pPrevNUFile = NULL;
|
|
if ((pFile->pNextNUFile = gv_FlmSysData.pMrnuFile) == NULL)
|
|
{
|
|
gv_FlmSysData.pLrnuFile = pFile;
|
|
}
|
|
else
|
|
{
|
|
pFile->pNextNUFile->pPrevNUFile = pFile;
|
|
}
|
|
|
|
gv_FlmSysData.pMrnuFile = pFile;
|
|
pFile->uiZeroUseCountTime = (FLMUINT)FLM_GET_TIMER();
|
|
}
|
|
else
|
|
{
|
|
pFile->pNextNUFile = NULL;
|
|
if ((pFile->pPrevNUFile = gv_FlmSysData.pLrnuFile) == NULL)
|
|
{
|
|
gv_FlmSysData.pMrnuFile = pFile;
|
|
}
|
|
else
|
|
{
|
|
pFile->pPrevNUFile->pNextNUFile = pFile;
|
|
}
|
|
|
|
gv_FlmSysData.pLrnuFile = pFile;
|
|
pFile->uiZeroUseCountTime = 0;
|
|
}
|
|
|
|
pFile->uiFlags |= DBF_IN_NU_LIST;
|
|
|
|
if (pFile->pRfl)
|
|
{
|
|
pFile->pRfl->closeFile();
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine unlinks an FFILE structure from the unused list.
|
|
NOTE: This function assumes that the global mutex has been
|
|
locked.
|
|
****************************************************************************/
|
|
void flmUnlinkFileFromNUList(
|
|
FFILE * pFile /* File to be unlinked from unused list. */
|
|
)
|
|
{
|
|
if (pFile->uiFlags & DBF_IN_NU_LIST)
|
|
{
|
|
if (!pFile->pPrevNUFile)
|
|
{
|
|
gv_FlmSysData.pMrnuFile = pFile->pNextNUFile;
|
|
}
|
|
else
|
|
{
|
|
pFile->pPrevNUFile->pNextNUFile = pFile->pNextNUFile;
|
|
}
|
|
if (!pFile->pNextNUFile)
|
|
{
|
|
gv_FlmSysData.pLrnuFile = pFile->pPrevNUFile;
|
|
}
|
|
else
|
|
{
|
|
pFile->pNextNUFile->pPrevNUFile = pFile->pPrevNUFile;
|
|
}
|
|
pFile->pPrevNUFile = pFile->pNextNUFile = (FFILE *)NULL;
|
|
pFile->uiFlags &= ~(DBF_IN_NU_LIST);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine checks unused structures to see if any have been unused
|
|
longer than the maximum unused time. If so, it frees them up.
|
|
Note: This routine assumes that the calling routine has locked the global
|
|
mutex prior to calling this routine. The mutex may be unlocked and
|
|
re-locked by one of the called routines.
|
|
****************************************************************************/
|
|
void flmCheckNUStructs(
|
|
FLMUINT uiCurrTime)
|
|
{
|
|
FFILE * pFile;
|
|
|
|
if (!uiCurrTime)
|
|
{
|
|
uiCurrTime = FLM_GET_TIMER();
|
|
}
|
|
|
|
// Look for unused FFILEs
|
|
|
|
pFile = gv_FlmSysData.pLrnuFile;
|
|
for (;;)
|
|
{
|
|
// Break out of the loop as soon as we discover an unused FFILE
|
|
// structure which has not been unused the maximum number of seconds.
|
|
|
|
if (pFile &&
|
|
(FLM_ELAPSED_TIME( uiCurrTime, pFile->uiZeroUseCountTime) >=
|
|
gv_FlmSysData.uiMaxUnusedTime || !pFile->uiZeroUseCountTime))
|
|
{
|
|
|
|
// Remove the FFILE from memory.
|
|
|
|
flmFreeFile( pFile);
|
|
|
|
// flmFreeFile may have unlocked (and re-locked the global mutex,
|
|
// so we need to start at the beginning of the list again.
|
|
|
|
// Need to unlock the mutex here in case another thread is in
|
|
// the process of closing the last FFILE. If we hang on to
|
|
// the mutex, it will never be able to get back in and finish
|
|
// the job.
|
|
|
|
f_mutexUnlock( gv_FlmSysData.hShareMutex);
|
|
f_yieldCPU();
|
|
f_mutexLock( gv_FlmSysData.hShareMutex);
|
|
pFile = gv_FlmSysData.pLrnuFile;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Look for unused file handles
|
|
|
|
if( gv_FlmSysData.pFileHdlMgr)
|
|
{
|
|
gv_FlmSysData.pFileHdlMgr->CheckAgedItems(
|
|
gv_FlmSysData.pFileHdlMgr->GetMaxAvailTime());
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine unlinks an FDICT structure from its FFILE structure and
|
|
then frees the FDICT structure.
|
|
NOTE: This routine assumes that the global mutex is locked.
|
|
****************************************************************************/
|
|
void flmUnlinkDict(
|
|
FDICT * pDict /* FDICT that is to be unlinked from its FFFILE
|
|
structure. */
|
|
)
|
|
{
|
|
/*
|
|
Now unlink the local dictionary from its file - if it is connected
|
|
to one.
|
|
*/
|
|
|
|
if (pDict->pFile)
|
|
{
|
|
if (pDict->pPrev)
|
|
{
|
|
pDict->pPrev->pNext = pDict->pNext;
|
|
}
|
|
else
|
|
{
|
|
pDict->pFile->pDictList = pDict->pNext;
|
|
}
|
|
if (pDict->pNext)
|
|
{
|
|
pDict->pNext->pPrev = pDict->pPrev;
|
|
}
|
|
}
|
|
|
|
/* Finally, free the local dictionary and its associated tables. */
|
|
|
|
flmFreeDict( pDict);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine links an FDB structure to an FFILE structure.
|
|
NOTE: This routine assumes that the global mutex has been
|
|
locked.
|
|
****************************************************************************/
|
|
RCODE flmLinkFdbToFile(
|
|
FDB * pDb, /* FDB that is to be linked to an FFILE
|
|
structure. */
|
|
FFILE * pFile /* Pointer to FFILE structure the FDB is to be
|
|
linked to. */
|
|
)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
|
|
/*
|
|
If the use count on the file used to be zero, unlink it from the
|
|
unused list.
|
|
*/
|
|
|
|
flmAssert( !pDb->pFile);
|
|
pDb->pPrevForFile = NULL;
|
|
if ((pDb->pNextForFile = pFile->pFirstDb) != NULL)
|
|
{
|
|
pFile->pFirstDb->pPrevForFile = pDb;
|
|
}
|
|
pFile->pFirstDb = pDb;
|
|
pDb->pFile = pFile;
|
|
if (++pFile->uiUseCount == 1)
|
|
{
|
|
flmUnlinkFileFromNUList( pFile);
|
|
}
|
|
if (pDb->uiFlags & FDB_INTERNAL_OPEN)
|
|
{
|
|
pFile->uiInternalUseCount++;
|
|
}
|
|
|
|
/*
|
|
Allocate the super file object
|
|
*/
|
|
|
|
if (!pDb->pSFileHdl)
|
|
{
|
|
if( (pDb->pSFileHdl = f_new F_SuperFileHdl) == NULL)
|
|
{
|
|
rc = RC_SET( FERR_MEM);
|
|
goto Exit;
|
|
}
|
|
|
|
/*
|
|
Set up the super file
|
|
*/
|
|
|
|
if( RC_BAD( rc = pDb->pSFileHdl->Setup( pFile->pFileIdList,
|
|
pFile->pszDbPath,
|
|
pFile->pszDataDir)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( pFile->pECacheMgr)
|
|
{
|
|
pDb->pSFileHdl->setECacheMgr( pFile->pECacheMgr);
|
|
}
|
|
|
|
if( pFile->FileHdr.uiVersionNum)
|
|
{
|
|
pDb->pSFileHdl->SetBlockSize( pFile->FileHdr.uiBlockSize);
|
|
pDb->pSFileHdl->SetDbVersion( pFile->FileHdr.uiVersionNum);
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine unlinks an FDB structure from its FFILE structure.
|
|
NOTE: This routine assumes that the global mutex has been
|
|
locked.
|
|
****************************************************************************/
|
|
void flmUnlinkFdbFromFile(
|
|
FDB * pDb)
|
|
{
|
|
FFILE * pFile;
|
|
|
|
if ((pFile = pDb->pFile) != NULL)
|
|
{
|
|
|
|
// Unlink the FDB from the FFILE.
|
|
|
|
if (pDb->pNextForFile)
|
|
{
|
|
pDb->pNextForFile->pPrevForFile = pDb->pPrevForFile;
|
|
}
|
|
if (pDb->pPrevForFile)
|
|
{
|
|
pDb->pPrevForFile->pNextForFile = pDb->pNextForFile;
|
|
}
|
|
else
|
|
{
|
|
pFile->pFirstDb = pDb->pNextForFile;
|
|
}
|
|
pDb->pNextForFile = pDb->pPrevForFile = NULL;
|
|
pDb->pFile = NULL;
|
|
|
|
// Decrement use counts in the FFILE.
|
|
|
|
if (pDb->uiFlags & FDB_INTERNAL_OPEN)
|
|
{
|
|
flmAssert( pFile->uiInternalUseCount);
|
|
pFile->uiInternalUseCount--;
|
|
}
|
|
|
|
// If the use count goes to zero on the file, put the file
|
|
// into the unused list.
|
|
|
|
flmAssert( pFile->uiUseCount);
|
|
if (!(--pFile->uiUseCount))
|
|
{
|
|
// If the "must close" flag is set, it indicates that
|
|
// the FFILE is being forced to close. Put the FFILE in
|
|
// the NU list, but specify that it should be quickly
|
|
// timed-out.
|
|
|
|
flmLinkFileToNUList( pFile, pFile->bMustClose);
|
|
}
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine functions as a thread. It monitors open files and
|
|
frees up files which have been closed longer than the maximum
|
|
close time.
|
|
****************************************************************************/
|
|
RCODE flmMonitor(
|
|
F_Thread * pThread)
|
|
{
|
|
FLMUINT uiLastUnusedCleanupTime = 0;
|
|
FLMUINT uiLastRCacheCleanupTime = 0;
|
|
FLMUINT uiLastSCacheCleanupTime = 0;
|
|
FLMUINT uiCurrTime;
|
|
FLMUINT uiMaxLockTime;
|
|
#ifdef FLM_CAN_GET_PHYS_MEM
|
|
FLMUINT uiLastCacheAdjustTime = 0;
|
|
#endif
|
|
|
|
FLM_MILLI_TO_TIMER_UNITS( 100, uiMaxLockTime);
|
|
|
|
for (;;)
|
|
{
|
|
|
|
/* See if we should shut down. */
|
|
|
|
if( pThread->getShutdownFlag())
|
|
{
|
|
break;
|
|
}
|
|
|
|
uiCurrTime = FLM_GET_TIMER();
|
|
|
|
// Check the not used stuff and lock timeouts.
|
|
|
|
if ( FLM_ELAPSED_TIME( uiCurrTime, uiLastUnusedCleanupTime) >=
|
|
gv_FlmSysData.uiUnusedCleanupInterval ||
|
|
(gv_FlmSysData.pLrnuFile &&
|
|
!gv_FlmSysData.pLrnuFile->uiZeroUseCountTime))
|
|
{
|
|
// See if any unused structures have bee unused longer than the
|
|
// maximum unused time. Free them if they have.
|
|
// May unlock and re-lock the global mutex.
|
|
|
|
f_mutexLock( gv_FlmSysData.hShareMutex);
|
|
flmCheckNUStructs( 0);
|
|
f_mutexUnlock( gv_FlmSysData.hShareMutex);
|
|
|
|
// Reset the timer
|
|
|
|
uiCurrTime = uiLastUnusedCleanupTime = FLM_GET_TIMER();
|
|
}
|
|
|
|
// Call the lock manager to check timeouts. It is critial
|
|
// that this routine be called on a regular interval to
|
|
// timeout lock waiters that have expired.
|
|
|
|
gv_FlmSysData.pServerLockMgr->CheckLockTimeouts( FALSE);
|
|
|
|
// Check the adjusting cache limit
|
|
|
|
#ifdef FLM_CAN_GET_PHYS_MEM
|
|
if ((gv_FlmSysData.bDynamicCacheAdjust) &&
|
|
(FLM_ELAPSED_TIME( uiCurrTime, uiLastCacheAdjustTime) >=
|
|
gv_FlmSysData.uiCacheAdjustInterval))
|
|
{
|
|
FLMUINT uiCacheBytes;
|
|
|
|
f_mutexLock( gv_FlmSysData.hShareMutex);
|
|
f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex);
|
|
|
|
// Make sure the dynamic adjust flag is still set.
|
|
|
|
if ((gv_FlmSysData.bDynamicCacheAdjust) &&
|
|
(FLM_ELAPSED_TIME( uiCurrTime, uiLastCacheAdjustTime) >=
|
|
gv_FlmSysData.uiCacheAdjustInterval))
|
|
{
|
|
uiCacheBytes = flmGetCacheBytes( gv_FlmSysData.uiCacheAdjustPercent,
|
|
gv_FlmSysData.uiCacheAdjustMin,
|
|
gv_FlmSysData.uiCacheAdjustMax,
|
|
gv_FlmSysData.uiCacheAdjustMinToLeave, TRUE,
|
|
gv_FlmSysData.SCacheMgr.Usage.uiTotalBytesAllocated +
|
|
gv_FlmSysData.RCacheMgr.pRCacheAlloc->getTotalBytesAllocated());
|
|
(void)flmSetCacheLimits( uiCacheBytes, FALSE);
|
|
}
|
|
f_mutexUnlock( gv_FlmSysData.hShareMutex);
|
|
f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex);
|
|
uiCurrTime = uiLastCacheAdjustTime = FLM_GET_TIMER();
|
|
}
|
|
#endif
|
|
|
|
// See if block cache should be cleaned up
|
|
|
|
if ((gv_FlmSysData.uiCacheCleanupInterval) &&
|
|
(FLM_ELAPSED_TIME( uiCurrTime, uiLastSCacheCleanupTime) >=
|
|
gv_FlmSysData.uiCacheCleanupInterval))
|
|
{
|
|
ScaCleanupCache( uiMaxLockTime);
|
|
uiCurrTime = uiLastSCacheCleanupTime = FLM_GET_TIMER();
|
|
}
|
|
|
|
// See if record cache should be cleaned up
|
|
|
|
if( (gv_FlmSysData.uiCacheCleanupInterval) &&
|
|
(FLM_ELAPSED_TIME( uiCurrTime, uiLastRCacheCleanupTime) >=
|
|
gv_FlmSysData.uiCacheCleanupInterval))
|
|
{
|
|
flmRcaCleanupCache( uiMaxLockTime, FALSE);
|
|
uiCurrTime = uiLastRCacheCleanupTime = FLM_GET_TIMER();
|
|
}
|
|
|
|
// Cleanup old sessions
|
|
|
|
if( gv_FlmSysData.pSessionMgr)
|
|
{
|
|
gv_FlmSysData.pSessionMgr->timeoutInactiveSessions(
|
|
MAX_SESSION_INACTIVE_SECS, FALSE);
|
|
}
|
|
|
|
f_sleep( 1000);
|
|
}
|
|
|
|
return( FERR_OK);
|
|
}
|
|
|
|
/*API~***********************************************************************
|
|
Desc : Registers a callback function to receive events.
|
|
*END************************************************************************/
|
|
RCODE FlmRegisterForEvent(
|
|
FEventCategory eCategory,
|
|
FEVENT_CB fnEventCB,
|
|
void * pvAppData,
|
|
HFEVENT * phEventRV)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FEVENT * pEvent;
|
|
|
|
*phEventRV = HFEVENT_NULL;
|
|
|
|
// Make sure it is a legal event category to register for.
|
|
|
|
if (eCategory >= F_MAX_EVENT_CATEGORIES)
|
|
{
|
|
rc = RC_SET( FERR_NOT_IMPLEMENTED);
|
|
goto Exit;
|
|
}
|
|
|
|
// Allocate an event structure
|
|
|
|
if (RC_BAD( rc = f_calloc( (FLMUINT)(sizeof( FEVENT)), &pEvent)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
*phEventRV = (HFEVENT)pEvent;
|
|
|
|
// Initialize the structure members and linkt to the
|
|
// list of events off of the event category.
|
|
|
|
pEvent->eCategory = eCategory;
|
|
pEvent->fnEventCB = fnEventCB;
|
|
pEvent->pvAppData = pvAppData;
|
|
// pEvent->pPrev = NULL; // done by flmAlloc above.
|
|
|
|
// Mutex should be locked to link into list.
|
|
|
|
f_mutexLock( gv_FlmSysData.EventHdrs [eCategory].hMutex);
|
|
if ((pEvent->pNext =
|
|
gv_FlmSysData.EventHdrs [eCategory].pEventCBList) != NULL)
|
|
{
|
|
pEvent->pNext->pPrev = pEvent;
|
|
}
|
|
gv_FlmSysData.EventHdrs [eCategory].pEventCBList = pEvent;
|
|
f_mutexUnlock( gv_FlmSysData.EventHdrs [eCategory].hMutex);
|
|
|
|
Exit:
|
|
return( rc);
|
|
}
|
|
|
|
/*API~***********************************************************************
|
|
Desc : Deregisters a callback function that was registered to receive events.
|
|
*END************************************************************************/
|
|
void FlmDeregisterForEvent(
|
|
HFEVENT * phEventRV)
|
|
{
|
|
if (phEventRV && *phEventRV != HFEVENT_NULL)
|
|
{
|
|
FEVENT * pEvent = (FEVENT *)(*phEventRV);
|
|
|
|
if (pEvent->eCategory < F_MAX_EVENT_CATEGORIES)
|
|
{
|
|
flmFreeEvent( pEvent,
|
|
gv_FlmSysData.EventHdrs [pEvent->eCategory].hMutex,
|
|
&gv_FlmSysData.EventHdrs [pEvent->eCategory].pEventCBList);
|
|
}
|
|
*phEventRV = HFEVENT_NULL;
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine does an event callback. Note that the mutex is
|
|
locked during the callback.
|
|
****************************************************************************/
|
|
void flmDoEventCallback(
|
|
FEventCategory eCategory,
|
|
FEventType eEventType,
|
|
void * pvEventData1,
|
|
void * pvEventData2)
|
|
{
|
|
FEVENT * pEvent;
|
|
|
|
f_mutexLock( gv_FlmSysData.EventHdrs [eCategory].hMutex);
|
|
pEvent = gv_FlmSysData.EventHdrs [eCategory].pEventCBList;
|
|
while (pEvent)
|
|
{
|
|
(*pEvent->fnEventCB)( eEventType, pEvent->pvAppData,
|
|
pvEventData1,
|
|
pvEventData2);
|
|
pEvent = pEvent->pNext;
|
|
}
|
|
f_mutexUnlock( gv_FlmSysData.EventHdrs [eCategory].hMutex);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine sets the "must close" flags on the FFILE and its FDBs
|
|
****************************************************************************/
|
|
void flmSetMustCloseFlags(
|
|
FFILE * pFile,
|
|
RCODE rcMustClose,
|
|
FLMBOOL bMutexLocked)
|
|
{
|
|
FDB * pTmpDb;
|
|
|
|
if( !bMutexLocked)
|
|
{
|
|
f_mutexLock( gv_FlmSysData.hShareMutex);
|
|
}
|
|
|
|
if( !pFile->bMustClose)
|
|
{
|
|
pFile->bMustClose = TRUE;
|
|
pFile->rcMustClose = rcMustClose;
|
|
pTmpDb = pFile->pFirstDb;
|
|
while( pTmpDb)
|
|
{
|
|
pTmpDb->bMustClose = TRUE;
|
|
pTmpDb = pTmpDb->pNextForFile;
|
|
}
|
|
|
|
// Log a message indicating why the "must close" flag has been
|
|
// set. Calling flmCheckFFileState with the bMustClose flag
|
|
// already set to TRUE will cause a message to be logged.
|
|
|
|
(void)flmCheckFFileState( pFile);
|
|
}
|
|
|
|
if( !bMutexLocked)
|
|
{
|
|
f_mutexUnlock( gv_FlmSysData.hShareMutex);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Constructor
|
|
****************************************************************************/
|
|
F_Session::F_Session()
|
|
{
|
|
m_pSessionMgr = NULL;
|
|
m_uiThreadId = 0;
|
|
m_uiThreadLockCount = 0;
|
|
m_hMutex = F_MUTEX_NULL;
|
|
m_pNotifyList = NULL;
|
|
m_pPrev = NULL;
|
|
m_pNext = NULL;
|
|
m_pNameTable = NULL;
|
|
m_uiNameTableFFileId = 0;
|
|
m_uiDictSeqNum = 0;
|
|
m_uiLastUsed = FLM_GET_TIMER();
|
|
m_uiNextToken = FLM_GET_TIMER();
|
|
m_pXmlImport = NULL;
|
|
m_pXmlExport = NULL;
|
|
m_pDbTable = NULL;
|
|
f_memset( m_ucKey, 0, sizeof( m_ucKey));
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Destructor
|
|
****************************************************************************/
|
|
F_Session::~F_Session()
|
|
{
|
|
flmAssert( !m_pPrev);
|
|
flmAssert( !m_pNext);
|
|
flmAssert( !m_ui32RefCnt);
|
|
flmAssert( !m_uiThreadLockCount);
|
|
|
|
// Wake up any waiters
|
|
|
|
signalLockWaiters( FERR_FAILURE, FALSE);
|
|
|
|
// Free the session mutex
|
|
|
|
if( m_hMutex != F_MUTEX_NULL)
|
|
{
|
|
f_mutexDestroy( &m_hMutex);
|
|
}
|
|
|
|
// Clean up any database objects
|
|
|
|
if( m_pDbTable)
|
|
{
|
|
m_pDbTable->Release();
|
|
}
|
|
|
|
// Free the name table
|
|
|
|
if( m_pNameTable)
|
|
{
|
|
m_pNameTable->Release();
|
|
}
|
|
|
|
// Free XML import / export
|
|
|
|
if( m_pXmlImport)
|
|
{
|
|
m_pXmlImport->Release();
|
|
}
|
|
|
|
if( m_pXmlExport)
|
|
{
|
|
m_pXmlExport->Release();
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Signals the next thread waiting to acquire the lock on the session
|
|
if rc == FERR_OK. Otherwise, signals all waiting threads.
|
|
****************************************************************************/
|
|
void F_Session::signalLockWaiters(
|
|
RCODE rc,
|
|
FLMBOOL bMutexLocked)
|
|
{
|
|
F_SEM hSem;
|
|
|
|
if( m_pNotifyList)
|
|
{
|
|
if( !bMutexLocked)
|
|
{
|
|
f_mutexLock( m_hMutex);
|
|
}
|
|
|
|
while( m_pNotifyList)
|
|
{
|
|
*(m_pNotifyList->pRc) = rc;
|
|
hSem = m_pNotifyList->hSem;
|
|
m_pNotifyList = m_pNotifyList->pNext;
|
|
f_semSignal( hSem);
|
|
if( RC_OK( rc))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( !bMutexLocked)
|
|
{
|
|
f_mutexUnlock( m_hMutex);
|
|
}
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Configures a session for use.
|
|
****************************************************************************/
|
|
RCODE F_Session::setupSession(
|
|
F_SessionMgr * pSessionMgr)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
|
|
flmAssert( m_hMutex == F_MUTEX_NULL);
|
|
flmAssert( pSessionMgr);
|
|
|
|
if( RC_BAD( rc = f_mutexCreate( &m_hMutex)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( (m_pXmlImport = f_new F_XMLImport) == NULL)
|
|
{
|
|
rc = RC_SET( FERR_MEM);
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( rc = m_pXmlImport->setup()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( (m_pXmlExport = f_new F_XMLExport) == NULL)
|
|
{
|
|
rc = RC_SET( FERR_MEM);
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( rc = m_pXmlExport->setup()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Allocate the object table
|
|
|
|
if( (m_pDbTable = f_new F_HashTable) == NULL)
|
|
{
|
|
rc = RC_SET( FERR_MEM);
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( rc = m_pDbTable->setupHashTable( FALSE, 16,
|
|
pSessionMgr->getCRCTable())))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
m_pSessionMgr = pSessionMgr;
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Adds a database handle to the session's list of handles. Once added
|
|
to the session, the calling code should not close the handle directly.
|
|
****************************************************************************/
|
|
RCODE F_Session::addDbHandle(
|
|
HFDB hDb,
|
|
char * pucKey)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
F_SessionDb * pSessionDb = NULL;
|
|
void * pvKey;
|
|
FLMUINT uiKeyLen;
|
|
|
|
if( (pSessionDb = f_new F_SessionDb) == NULL)
|
|
{
|
|
rc = RC_SET( FERR_MEM);
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( rc = pSessionDb->setupSessionDb( this, hDb)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( rc = m_pDbTable->addObject( pSessionDb)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( pucKey)
|
|
{
|
|
pvKey = pSessionDb->getKey( &uiKeyLen);
|
|
flmAssert( uiKeyLen == F_SESSION_DB_KEY_LEN);
|
|
f_memcpy( pucKey, (FLMBYTE *)pvKey, uiKeyLen);
|
|
}
|
|
|
|
pSessionDb->Release();
|
|
pSessionDb = NULL;
|
|
|
|
Exit:
|
|
|
|
if( pSessionDb)
|
|
{
|
|
pSessionDb->m_hDb = HFDB_NULL;
|
|
pSessionDb->Release();
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Closes the specific database identified by the passed-in key
|
|
****************************************************************************/
|
|
void F_Session::closeDb(
|
|
const char * pucKey)
|
|
{
|
|
(void)m_pDbTable->getObject( (void *)pucKey, F_SESSION_DB_KEY_LEN,
|
|
NULL, TRUE);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Gets a specific database handle from the session given the passed-in
|
|
key
|
|
****************************************************************************/
|
|
RCODE F_Session::getDbHandle(
|
|
const char * pucKey,
|
|
HFDB * phDb)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
F_SessionDb * pSessionDb = NULL;
|
|
F_HashObject * pObject;
|
|
|
|
*phDb = HFDB_NULL;
|
|
|
|
if( RC_BAD( rc = m_pDbTable->getObject( (void *)pucKey, F_SESSION_DB_KEY_LEN,
|
|
&pObject, FALSE)))
|
|
{
|
|
if( rc == FERR_NOT_FOUND)
|
|
{
|
|
rc = RC_SET( FERR_BAD_HDL);
|
|
}
|
|
|
|
goto Exit;
|
|
}
|
|
|
|
pSessionDb = (F_SessionDb *)pObject;
|
|
*phDb = pSessionDb->m_hDb;
|
|
|
|
Exit:
|
|
|
|
if( pSessionDb)
|
|
{
|
|
pSessionDb->Release();
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Returns the next database handle in the global list of handles managed
|
|
by the session.
|
|
****************************************************************************/
|
|
RCODE F_Session::getNextDb(
|
|
F_SessionDb ** ppSessionDb)
|
|
{
|
|
F_HashObject * pObject = *ppSessionDb;
|
|
RCODE rc = FERR_OK;
|
|
|
|
if( RC_BAD( rc = m_pDbTable->getNextObjectInGlobal( &pObject)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
*ppSessionDb = (F_SessionDb *)pObject;
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Releases any session resources associated with the specified FFILE
|
|
****************************************************************************/
|
|
void F_Session::releaseFileResources(
|
|
FFILE * pFile)
|
|
{
|
|
F_HashObject * pObject;
|
|
F_HashObject * pNextObject;
|
|
F_SessionDb * pSessionDb;
|
|
|
|
// Close all database handles with the specified FFILE
|
|
|
|
pObject = NULL;
|
|
if( RC_BAD( m_pDbTable->getNextObjectInGlobal( &pObject)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
while( pObject)
|
|
{
|
|
if( (pNextObject = pObject->getNextInGlobal()) != NULL)
|
|
{
|
|
pNextObject->AddRef();
|
|
}
|
|
|
|
if( pObject->objectType() == HASH_DB_OBJ)
|
|
{
|
|
pSessionDb = (F_SessionDb *)pObject;
|
|
|
|
if( ((FDB *)pSessionDb->getDbHandle())->pFile == pFile)
|
|
{
|
|
closeDb( (const char *)pSessionDb->getKey());
|
|
}
|
|
}
|
|
pObject->Release();
|
|
pObject = pNextObject;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Returns a pointer to a name table generated based on the supplied
|
|
database handle
|
|
****************************************************************************/
|
|
RCODE F_Session::getNameTable(
|
|
HFDB hDb,
|
|
F_NameTable ** ppNameTable)
|
|
{
|
|
FLMUINT uiSeq;
|
|
FLMUINT uiFFileId;
|
|
RCODE rc = FERR_OK;
|
|
|
|
if( !m_pNameTable)
|
|
{
|
|
if( (m_pNameTable = f_new F_NameTable) == NULL)
|
|
{
|
|
rc = RC_SET( FERR_MEM);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if( RC_BAD( rc = FlmDbGetConfig( hDb,
|
|
FDB_GET_DICT_SEQ_NUM, (void *)&uiSeq)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( rc = FlmDbGetConfig( hDb,
|
|
FDB_GET_FFILE_ID, (void *)&uiFFileId)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// If the database handle does not reference the same
|
|
// database or dictionary as the last time the name table
|
|
// was refreshed, we need to re-populate the table.
|
|
|
|
if( uiSeq != m_uiDictSeqNum ||
|
|
m_uiNameTableFFileId != uiFFileId)
|
|
{
|
|
if( RC_BAD( rc = m_pNameTable->setupFromDb( hDb)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
m_uiDictSeqNum = uiSeq;
|
|
m_uiNameTableFFileId = uiFFileId;
|
|
}
|
|
|
|
*ppNameTable = m_pNameTable;
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Returns a pointer to a name table generated based on the supplied
|
|
FFILE.
|
|
****************************************************************************/
|
|
RCODE F_Session::getNameTable(
|
|
FFILE * pFile,
|
|
F_NameTable ** ppNameTable)
|
|
{
|
|
FDB * pDb = NULL;
|
|
RCODE rc = FERR_OK;
|
|
|
|
// Temporarily open the database
|
|
|
|
if( RC_BAD( rc = flmOpenFile( pFile, NULL, NULL, NULL, 0,
|
|
TRUE, NULL, NULL,
|
|
pFile->pszDbPassword, &pDb)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Get the name table
|
|
|
|
if( RC_BAD( rc = getNameTable( (HFDB)pDb, ppNameTable)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
|
|
if( pDb)
|
|
{
|
|
(void) FlmDbClose( (HFDB *)&pDb);
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Returns a unique token generated by the session manager. Tokens are
|
|
used to help make handles passed to clients unique across server
|
|
executions
|
|
****************************************************************************/
|
|
FLMUINT F_Session::getNextToken( void)
|
|
{
|
|
return( m_pSessionMgr->getNextToken());
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Locks a session for use by a thread. If bWait is TRUE, the thread
|
|
will be put into a queue until the lock can be granted. This routine
|
|
can be called multiple times by the same thread, as long as
|
|
unlockSession is called a corresponding number of times.
|
|
****************************************************************************/
|
|
RCODE F_Session::lockSession(
|
|
FLMBOOL bWait)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
|
|
flmAssert( m_ui32RefCnt);
|
|
f_mutexLock( m_hMutex);
|
|
|
|
if( m_uiThreadId && m_uiThreadId != f_threadId())
|
|
{
|
|
if( !bWait)
|
|
{
|
|
rc = RC_SET( FERR_TIMEOUT);
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( rc = flmWaitNotifyReq( m_hMutex, &m_pNotifyList, NULL)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
m_uiThreadId = f_threadId();
|
|
m_uiThreadLockCount++;
|
|
|
|
Exit:
|
|
|
|
f_mutexUnlock( m_hMutex);
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Releases a thread's lock on a session.
|
|
****************************************************************************/
|
|
void F_Session::unlockSession()
|
|
{
|
|
F_SEM hSem;
|
|
|
|
flmAssert( m_ui32RefCnt);
|
|
|
|
f_mutexLock( m_hMutex);
|
|
if( m_uiThreadId != f_threadId())
|
|
{
|
|
flmAssert( 0);
|
|
}
|
|
else
|
|
{
|
|
if( --m_uiThreadLockCount == 0)
|
|
{
|
|
m_uiThreadId = 0;
|
|
|
|
if( m_pNotifyList)
|
|
{
|
|
*(m_pNotifyList->pRc) = FERR_OK;
|
|
hSem = m_pNotifyList->hSem;
|
|
m_pNotifyList = m_pNotifyList->pNext;
|
|
f_semSignal( hSem);
|
|
}
|
|
}
|
|
|
|
m_uiLastUsed = FLM_GET_TIMER();
|
|
}
|
|
f_mutexUnlock( m_hMutex);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Gets the session object's hash key
|
|
****************************************************************************/
|
|
void * F_Session::getKey(
|
|
FLMUINT * puiKeyLen)
|
|
{
|
|
if( puiKeyLen)
|
|
{
|
|
*puiKeyLen = (FLMUINT)sizeof( m_ucKey);
|
|
}
|
|
return( (void *)(&m_ucKey[ 0]));
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Adds a reference to the session object. The mutex is locked prior
|
|
to incrementing the count since multiple threads are allowed to
|
|
acquire a pointer to the object. However, they shouldn't use the
|
|
object w/o first locking it via a call to lockSession.
|
|
****************************************************************************/
|
|
FLMUINT F_Session::AddRef( void)
|
|
{
|
|
FLMUINT uiRefCnt;
|
|
|
|
f_mutexLock( m_hMutex);
|
|
flmAssert( m_ui32RefCnt);
|
|
uiRefCnt = F_Base::AddRef();
|
|
f_mutexUnlock( m_hMutex);
|
|
|
|
return( uiRefCnt);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Decrements the objects use count
|
|
****************************************************************************/
|
|
FLMUINT F_Session::Release( void)
|
|
{
|
|
FLMUINT uiRefCnt;
|
|
|
|
flmAssert( m_ui32RefCnt);
|
|
|
|
f_mutexLock( m_hMutex);
|
|
if( (uiRefCnt = --m_ui32RefCnt) == 0)
|
|
{
|
|
f_mutexUnlock( m_hMutex);
|
|
delete this;
|
|
return( uiRefCnt);
|
|
}
|
|
f_mutexUnlock( m_hMutex);
|
|
|
|
return( uiRefCnt);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Destructor
|
|
****************************************************************************/
|
|
F_SessionMgr::~F_SessionMgr()
|
|
{
|
|
if( m_pSessionTable)
|
|
{
|
|
shutdownSessions();
|
|
m_pSessionTable->Release();
|
|
}
|
|
|
|
if( m_hMutex != F_MUTEX_NULL)
|
|
{
|
|
f_mutexDestroy( &m_hMutex);
|
|
}
|
|
|
|
if( m_pCRCTable)
|
|
{
|
|
f_freeCRCTable( &m_pCRCTable);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Releases all resources (database handles, etc.) in all sessions
|
|
if they are tied to the specified FFILE
|
|
****************************************************************************/
|
|
void F_SessionMgr::releaseFileResources(
|
|
FFILE * pFile)
|
|
{
|
|
F_Session * pSession;
|
|
F_HashObject * pObject;
|
|
FLMBOOL bMutexLocked = FALSE;
|
|
|
|
if( m_hMutex == F_MUTEX_NULL)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
f_mutexLock( m_hMutex);
|
|
bMutexLocked = TRUE;
|
|
|
|
pObject = NULL;
|
|
if( RC_BAD( m_pSessionTable->getNextObjectInGlobal( &pObject)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
while( pObject)
|
|
{
|
|
flmAssert( pObject->objectType() == HASH_SESSION_OBJ);
|
|
|
|
pSession = (F_Session *)pObject;
|
|
if( (pObject = pObject->getNextInGlobal()) != NULL)
|
|
{
|
|
pObject->AddRef();
|
|
}
|
|
|
|
if( RC_OK( pSession->lockSession()))
|
|
{
|
|
pSession->releaseFileResources( pFile);
|
|
pSession->unlockSession();
|
|
}
|
|
pSession->Release();
|
|
}
|
|
|
|
Exit:
|
|
if (bMutexLocked)
|
|
{
|
|
f_mutexUnlock( m_hMutex);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Shuts down all sessions being managed
|
|
****************************************************************************/
|
|
void F_SessionMgr::shutdownSessions()
|
|
{
|
|
F_Session * pSession;
|
|
F_HashObject * pObject;
|
|
FLMBOOL bMutexLocked = FALSE;
|
|
|
|
if( m_hMutex == F_MUTEX_NULL)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
f_mutexLock( m_hMutex);
|
|
bMutexLocked = TRUE;
|
|
|
|
pObject = NULL;
|
|
if( RC_BAD( m_pSessionTable->getNextObjectInGlobal( &pObject)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
while( pObject)
|
|
{
|
|
flmAssert( pObject->objectType() == HASH_SESSION_OBJ);
|
|
|
|
pSession = (F_Session *)pObject;
|
|
if( (pObject = pObject->getNextInGlobal()) != NULL)
|
|
{
|
|
pObject->AddRef();
|
|
}
|
|
|
|
if( RC_OK( pSession->lockSession()))
|
|
{
|
|
m_pSessionTable->removeObject( pSession);
|
|
pSession->signalLockWaiters( FERR_FAILURE, FALSE);
|
|
pSession->unlockSession();
|
|
}
|
|
pSession->Release();
|
|
}
|
|
|
|
Exit:
|
|
if (bMutexLocked)
|
|
{
|
|
f_mutexUnlock( m_hMutex);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Configures the session manager prior to use
|
|
****************************************************************************/
|
|
RCODE F_SessionMgr::setupSessionMgr( void)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
|
|
flmAssert( m_hMutex == F_MUTEX_NULL);
|
|
|
|
// Create the mutex
|
|
|
|
if( RC_BAD( rc = f_mutexCreate( &m_hMutex)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Initialize the CRC table
|
|
|
|
if( RC_BAD( rc = f_initCRCTable( &m_pCRCTable)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Create the session object table
|
|
|
|
if( (m_pSessionTable = f_new F_HashTable) == NULL)
|
|
{
|
|
rc = RC_SET( FERR_MEM);
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( rc = m_pSessionTable->setupHashTable( FALSE,
|
|
128, m_pCRCTable)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Gets a session given a session key
|
|
****************************************************************************/
|
|
RCODE F_SessionMgr::getSession(
|
|
const char * pszKey,
|
|
F_Session ** ppSession)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
F_Session * pSession = NULL;
|
|
F_HashObject * pObject;
|
|
FLMBOOL bMutexLocked = FALSE;
|
|
|
|
*ppSession = NULL;
|
|
|
|
f_mutexLock( m_hMutex);
|
|
bMutexLocked = TRUE;
|
|
|
|
if( RC_BAD( rc = m_pSessionTable->getObject( (void *)pszKey,
|
|
F_SESSION_KEY_LEN, &pObject, FALSE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// NOTE: getObject() does an addRef for the caller.
|
|
|
|
pSession = (F_Session *)pObject;
|
|
|
|
f_mutexUnlock( m_hMutex);
|
|
bMutexLocked = FALSE;
|
|
|
|
if( RC_BAD( rc = pSession->lockSession()))
|
|
{
|
|
pSession->Release();
|
|
goto Exit;
|
|
}
|
|
|
|
*ppSession = pSession;
|
|
|
|
Exit:
|
|
|
|
if( bMutexLocked)
|
|
{
|
|
f_mutexUnlock( m_hMutex);
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Unlocks and releases a session being used by a thread
|
|
****************************************************************************/
|
|
void F_SessionMgr::releaseSession(
|
|
F_Session ** ppSession)
|
|
{
|
|
(*ppSession)->unlockSession();
|
|
(*ppSession)->Release();
|
|
*ppSession = NULL;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Creates a new session
|
|
****************************************************************************/
|
|
RCODE F_SessionMgr::createSession(
|
|
F_Session ** ppSession)
|
|
{
|
|
F_Session * pNewSession = NULL;
|
|
FLMBOOL bMutexLocked = FALSE;
|
|
RCODE rc = FERR_OK;
|
|
|
|
if( (pNewSession = f_new F_Session) == NULL)
|
|
{
|
|
rc = RC_SET( FERR_MEM);
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( rc = pNewSession->setupSession( this)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
f_mutexLock( m_hMutex);
|
|
bMutexLocked = TRUE;
|
|
|
|
// Session ID
|
|
|
|
f_sprintf( (char *)&pNewSession->m_ucKey[ 0], "%0*X",
|
|
(int)(sizeof( FLMUINT) * 2),
|
|
(unsigned)m_uiNextId++);
|
|
|
|
// Token
|
|
|
|
f_sprintf( (char *)&pNewSession->m_ucKey[ sizeof( FLMUINT) * 2], "%0*X",
|
|
(int)(sizeof( FLMUINT) * 2),
|
|
(unsigned)m_uiNextToken++);
|
|
|
|
pNewSession->m_ucKey[ sizeof( pNewSession->m_ucKey) - 1] = 0;
|
|
|
|
// Add the session to the table
|
|
|
|
if( RC_BAD( rc = m_pSessionTable->addObject(
|
|
pNewSession)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
f_mutexUnlock( m_hMutex);
|
|
bMutexLocked = FALSE;
|
|
|
|
if( RC_BAD( rc = pNewSession->lockSession()))
|
|
{
|
|
pNewSession->Release();
|
|
goto Exit;
|
|
}
|
|
|
|
*ppSession = pNewSession;
|
|
pNewSession = NULL;
|
|
|
|
Exit:
|
|
|
|
if( pNewSession)
|
|
{
|
|
pNewSession->Release();
|
|
}
|
|
|
|
if( bMutexLocked)
|
|
{
|
|
f_mutexUnlock( m_hMutex);
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Kills any unused sessions that have been inactive for the specified
|
|
number of seconds
|
|
****************************************************************************/
|
|
void F_SessionMgr::timeoutInactiveSessions(
|
|
FLMUINT uiInactiveSecs,
|
|
FLMBOOL bWaitForLocks)
|
|
{
|
|
F_Session * pSession;
|
|
F_HashObject * pObject;
|
|
FLMUINT uiCurrTime;
|
|
FLMUINT uiElapTime;
|
|
FLMUINT uiElapSecs;
|
|
FLMBOOL bMutexLocked = FALSE;
|
|
|
|
f_mutexLock( m_hMutex);
|
|
bMutexLocked = TRUE;
|
|
|
|
pObject = NULL;
|
|
if( RC_BAD( m_pSessionTable->getNextObjectInGlobal( &pObject)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
while( pObject)
|
|
{
|
|
flmAssert( pObject->objectType() == HASH_SESSION_OBJ);
|
|
|
|
pSession = (F_Session *)pObject;
|
|
if( (pObject = pObject->getNextInGlobal()) != NULL)
|
|
{
|
|
pObject->AddRef();
|
|
}
|
|
|
|
if( RC_OK( pSession->lockSession( bWaitForLocks)))
|
|
{
|
|
uiCurrTime = FLM_GET_TIMER();
|
|
uiElapTime = FLM_ELAPSED_TIME( uiCurrTime, pSession->m_uiLastUsed);
|
|
FLM_TIMER_UNITS_TO_SECS( uiElapTime, uiElapSecs);
|
|
|
|
if( !uiInactiveSecs || uiElapSecs >= uiInactiveSecs)
|
|
{
|
|
m_pSessionTable->removeObject( pSession);
|
|
pSession->signalLockWaiters( FERR_FAILURE, FALSE);
|
|
}
|
|
pSession->unlockSession();
|
|
}
|
|
pSession->Release();
|
|
}
|
|
|
|
Exit:
|
|
|
|
if( bMutexLocked)
|
|
{
|
|
f_mutexUnlock( m_hMutex);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Constructor
|
|
****************************************************************************/
|
|
F_HashTable::F_HashTable()
|
|
{
|
|
m_hMutex = F_MUTEX_NULL;
|
|
m_pGlobalList = NULL;
|
|
m_ppHashTable = NULL;
|
|
m_uiBuckets = 0;
|
|
m_pCRCTable = NULL;
|
|
m_bOwnCRCTable = FALSE;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Destructor
|
|
****************************************************************************/
|
|
F_HashTable::~F_HashTable()
|
|
{
|
|
F_HashObject * pCur;
|
|
F_HashObject * pNext;
|
|
|
|
pCur = m_pGlobalList;
|
|
while( pCur)
|
|
{
|
|
pNext = pCur->m_pNextInGlobal;
|
|
unlinkObject( pCur);
|
|
pCur->Release();
|
|
pCur = pNext;
|
|
}
|
|
|
|
if( m_ppHashTable)
|
|
{
|
|
f_free( &m_ppHashTable);
|
|
}
|
|
|
|
if( m_hMutex != F_MUTEX_NULL)
|
|
{
|
|
f_mutexDestroy( &m_hMutex);
|
|
}
|
|
|
|
if( m_pCRCTable && m_bOwnCRCTable)
|
|
{
|
|
f_freeCRCTable( &m_pCRCTable);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Configures the hash table prior to first use
|
|
****************************************************************************/
|
|
RCODE F_HashTable::setupHashTable(
|
|
FLMBOOL bMultithreaded,
|
|
FLMUINT uiNumBuckets,
|
|
FLMUINT32 * pCRCTable)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
|
|
flmAssert( uiNumBuckets);
|
|
|
|
// Create the hash table
|
|
|
|
if( RC_BAD( rc = f_alloc(
|
|
sizeof( F_HashObject *) * uiNumBuckets, &m_ppHashTable)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
m_uiBuckets = uiNumBuckets;
|
|
f_memset( m_ppHashTable, 0, sizeof( F_HashObject *) * uiNumBuckets);
|
|
|
|
if( bMultithreaded)
|
|
{
|
|
// Initialize the mutex
|
|
|
|
if( RC_BAD( rc = f_mutexCreate( &m_hMutex)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if( !pCRCTable)
|
|
{
|
|
// Initialize the CRC table
|
|
|
|
if( RC_BAD( rc = f_initCRCTable( &m_pCRCTable)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
m_bOwnCRCTable = TRUE;
|
|
}
|
|
else
|
|
{
|
|
m_pCRCTable = pCRCTable;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Retrieves an object from the hash table with the specified key.
|
|
This routine assumes the table's mutex has already been locked.
|
|
A reference IS NOT added to the object for the caller.
|
|
****************************************************************************/
|
|
RCODE F_HashTable::findObject(
|
|
void * pvKey,
|
|
FLMUINT uiKeyLen,
|
|
F_HashObject ** ppObject)
|
|
{
|
|
F_HashObject * pObject = NULL;
|
|
FLMUINT uiBucket;
|
|
FLMUINT32 ui32CRC = 0;
|
|
RCODE rc = FERR_OK;
|
|
|
|
*ppObject = NULL;
|
|
|
|
// Calculate the hash bucket and mutex offset
|
|
|
|
uiBucket = getHashBucket( pvKey, uiKeyLen, &ui32CRC);
|
|
|
|
// Search the bucket for an object with a matching
|
|
// key.
|
|
|
|
pObject = m_ppHashTable[ uiBucket];
|
|
while( pObject)
|
|
{
|
|
if( pObject->getKeyCRC() == ui32CRC)
|
|
{
|
|
void * pvTmpKey;
|
|
FLMUINT uiTmpKeyLen;
|
|
|
|
pvTmpKey = pObject->getKey( &uiTmpKeyLen);
|
|
if( uiTmpKeyLen == uiKeyLen &&
|
|
f_memcmp( pvTmpKey, pvKey, uiKeyLen) == 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
pObject = pObject->m_pNextInBucket;
|
|
}
|
|
|
|
if( !pObject)
|
|
{
|
|
rc = RC_SET( FERR_NOT_FOUND);
|
|
goto Exit;
|
|
}
|
|
|
|
*ppObject = pObject;
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Adds an object to the hash table
|
|
****************************************************************************/
|
|
RCODE F_HashTable::addObject(
|
|
F_HashObject * pObject)
|
|
{
|
|
FLMUINT uiBucket;
|
|
F_HashObject * pTmp;
|
|
void * pvKey;
|
|
FLMUINT uiKeyLen;
|
|
FLMUINT32 ui32CRC;
|
|
FLMBOOL bMutexLocked = FALSE;
|
|
RCODE rc = FERR_OK;
|
|
|
|
// Calculate and set the objects hash bucket
|
|
|
|
flmAssert( pObject->getHashBucket() == F_INVALID_HASH_BUCKET);
|
|
|
|
pvKey = pObject->getKey( &uiKeyLen);
|
|
flmAssert( uiKeyLen);
|
|
|
|
uiBucket = getHashBucket( pvKey, uiKeyLen, &ui32CRC);
|
|
pObject->setKeyCRC( ui32CRC);
|
|
|
|
// Lock the mutex
|
|
|
|
if( m_hMutex != F_MUTEX_NULL)
|
|
{
|
|
f_mutexLock( m_hMutex);
|
|
bMutexLocked = TRUE;
|
|
}
|
|
|
|
// Make sure the object doesn't already exist
|
|
|
|
if( RC_BAD( rc = findObject( pvKey, uiKeyLen, &pTmp)))
|
|
{
|
|
if( rc != FERR_NOT_FOUND)
|
|
{
|
|
goto Exit;
|
|
}
|
|
rc = FERR_OK;
|
|
}
|
|
else
|
|
{
|
|
flmAssert( 0);
|
|
rc = RC_SET( FERR_EXISTS);
|
|
goto Exit;
|
|
}
|
|
|
|
// Add a reference to the object
|
|
|
|
pObject->AddRef();
|
|
|
|
// Link the object into the appropriate lists
|
|
|
|
linkObject( pObject, uiBucket);
|
|
|
|
Exit:
|
|
|
|
// Unlock the mutex
|
|
|
|
if( bMutexLocked)
|
|
{
|
|
f_mutexUnlock( m_hMutex);
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Returns the next object in the linked list of objects in the hash
|
|
table. If *ppObject == NULL, the first object will be returned.
|
|
****************************************************************************/
|
|
RCODE F_HashTable::getNextObjectInGlobal(
|
|
F_HashObject ** ppObject)
|
|
{
|
|
FLMBOOL bMutexLocked = FALSE;
|
|
F_HashObject * pOldObj;
|
|
RCODE rc = FERR_OK;
|
|
|
|
// Lock the mutex
|
|
|
|
if( m_hMutex != F_MUTEX_NULL)
|
|
{
|
|
f_mutexLock( m_hMutex);
|
|
bMutexLocked = TRUE;
|
|
}
|
|
|
|
if( !(*ppObject))
|
|
{
|
|
*ppObject = m_pGlobalList;
|
|
}
|
|
else
|
|
{
|
|
pOldObj = *ppObject;
|
|
*ppObject = (*ppObject)->m_pNextInGlobal;
|
|
pOldObj->Release();
|
|
}
|
|
|
|
if( *ppObject == NULL)
|
|
{
|
|
rc = RC_SET( FERR_EOF_HIT);
|
|
goto Exit;
|
|
}
|
|
|
|
(*ppObject)->AddRef();
|
|
|
|
Exit:
|
|
|
|
if( bMutexLocked)
|
|
{
|
|
f_mutexUnlock( m_hMutex);
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Retrieves an object from the hash table with the specified key
|
|
****************************************************************************/
|
|
RCODE F_HashTable::getObject(
|
|
void * pvKey,
|
|
FLMUINT uiKeyLen,
|
|
F_HashObject ** ppObject,
|
|
FLMBOOL bRemove)
|
|
{
|
|
F_HashObject * pObject;
|
|
FLMBOOL bMutexLocked = FALSE;
|
|
RCODE rc = FERR_OK;
|
|
|
|
// Lock the mutex
|
|
|
|
if( m_hMutex != F_MUTEX_NULL)
|
|
{
|
|
f_mutexLock( m_hMutex);
|
|
bMutexLocked = TRUE;
|
|
}
|
|
|
|
// Search for an object with a matching key.
|
|
|
|
if( RC_BAD( rc = findObject( pvKey, uiKeyLen, &pObject)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( pObject && bRemove)
|
|
{
|
|
unlinkObject( pObject);
|
|
if( !ppObject)
|
|
{
|
|
pObject->Release();
|
|
pObject = NULL;
|
|
}
|
|
}
|
|
|
|
if( ppObject)
|
|
{
|
|
if( !bRemove)
|
|
{
|
|
pObject->AddRef();
|
|
}
|
|
*ppObject = pObject;
|
|
pObject = NULL;
|
|
}
|
|
|
|
Exit:
|
|
|
|
if( bMutexLocked)
|
|
{
|
|
f_mutexUnlock( m_hMutex);
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Removes an object from the hash table by key
|
|
****************************************************************************/
|
|
RCODE F_HashTable::removeObject(
|
|
void * pvKey,
|
|
FLMUINT uiKeyLen)
|
|
{
|
|
return( getObject( pvKey, uiKeyLen, NULL, TRUE));
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Removes an object from the hash table by object pointer
|
|
****************************************************************************/
|
|
RCODE F_HashTable::removeObject(
|
|
F_HashObject * pObject)
|
|
{
|
|
FLMUINT uiKeyLen;
|
|
void * pvKey;
|
|
|
|
pvKey = pObject->getKey( &uiKeyLen);
|
|
return( getObject( pvKey, uiKeyLen, NULL, TRUE));
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Calculates the hash bucket of a key and optionally returns the key's
|
|
CRC.
|
|
****************************************************************************/
|
|
FLMUINT F_HashTable::getHashBucket(
|
|
void * pvKey,
|
|
FLMUINT uiLen,
|
|
FLMUINT32 * pui32KeyCRC)
|
|
{
|
|
FLMUINT32 ui32CRC = 0;
|
|
|
|
f_updateCRC( m_pCRCTable, (FLMBYTE *)pvKey, uiLen, &ui32CRC);
|
|
if( pui32KeyCRC)
|
|
{
|
|
*pui32KeyCRC = ui32CRC;
|
|
}
|
|
return( ui32CRC % m_uiBuckets);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Links an object to the global list and also to its bucket
|
|
Notes: This routine assumes that the bucket's mutex is already locked
|
|
if the hash table is multi-threaded.
|
|
****************************************************************************/
|
|
void F_HashTable::linkObject(
|
|
F_HashObject * pObject,
|
|
FLMUINT uiBucket)
|
|
{
|
|
flmAssert( uiBucket < m_uiBuckets);
|
|
flmAssert( pObject->getHashBucket() == F_INVALID_HASH_BUCKET);
|
|
|
|
// Set the object's bucket
|
|
|
|
pObject->setHashBucket( uiBucket);
|
|
|
|
// Link the object to its hash bucket
|
|
|
|
pObject->m_pNextInBucket = m_ppHashTable[ uiBucket];
|
|
if( m_ppHashTable[ uiBucket])
|
|
{
|
|
m_ppHashTable[ uiBucket]->m_pPrevInBucket = pObject;
|
|
}
|
|
m_ppHashTable[ uiBucket] = pObject;
|
|
|
|
// Link to the global list
|
|
|
|
pObject->m_pNextInGlobal = m_pGlobalList;
|
|
if( m_pGlobalList)
|
|
{
|
|
m_pGlobalList->m_pPrevInGlobal = pObject;
|
|
}
|
|
m_pGlobalList = pObject;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Unlinks an object from its bucket and the global list.
|
|
Notes: This routine assumes that the bucket's mutex is already locked
|
|
if the hash table is multi-threaded.
|
|
****************************************************************************/
|
|
void F_HashTable::unlinkObject(
|
|
F_HashObject * pObject)
|
|
{
|
|
FLMUINT uiBucket = pObject->getHashBucket();
|
|
|
|
// Is the bucket valid?
|
|
|
|
flmAssert( uiBucket < m_uiBuckets);
|
|
|
|
// Unlink from the hash bucket
|
|
|
|
if( pObject->m_pNextInBucket)
|
|
{
|
|
pObject->m_pNextInBucket->m_pPrevInBucket = pObject->m_pPrevInBucket;
|
|
}
|
|
|
|
if( pObject->m_pPrevInBucket)
|
|
{
|
|
pObject->m_pPrevInBucket->m_pNextInBucket = pObject->m_pNextInBucket;
|
|
}
|
|
else
|
|
{
|
|
m_ppHashTable[ uiBucket] = pObject->m_pNextInBucket;
|
|
}
|
|
|
|
pObject->m_pPrevInBucket = NULL;
|
|
pObject->m_pNextInBucket = NULL;
|
|
pObject->setHashBucket( F_INVALID_HASH_BUCKET);
|
|
|
|
// Unlink from the global list
|
|
|
|
if( pObject->m_pNextInGlobal)
|
|
{
|
|
pObject->m_pNextInGlobal->m_pPrevInGlobal = pObject->m_pPrevInGlobal;
|
|
}
|
|
|
|
if( pObject->m_pPrevInGlobal)
|
|
{
|
|
pObject->m_pPrevInGlobal->m_pNextInGlobal = pObject->m_pNextInGlobal;
|
|
}
|
|
else
|
|
{
|
|
m_pGlobalList = pObject->m_pNextInGlobal;
|
|
}
|
|
|
|
pObject->m_pPrevInGlobal = NULL;
|
|
pObject->m_pNextInGlobal = NULL;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Constructor
|
|
****************************************************************************/
|
|
F_SessionDb::F_SessionDb()
|
|
{
|
|
m_hDb = HFDB_NULL;
|
|
f_memset( m_ucKey, 0, sizeof( m_ucKey));
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Destructor
|
|
****************************************************************************/
|
|
F_SessionDb::~F_SessionDb()
|
|
{
|
|
if( m_hDb != HFDB_NULL)
|
|
{
|
|
FlmDbClose( &m_hDb);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Configures a database object prior to being used
|
|
****************************************************************************/
|
|
RCODE F_SessionDb::setupSessionDb(
|
|
F_Session * pSession,
|
|
HFDB hDb)
|
|
{
|
|
flmAssert( hDb != HFDB_NULL);
|
|
flmAssert( m_hDb == HFDB_NULL);
|
|
|
|
m_pSession = pSession;
|
|
m_hDb = hDb;
|
|
|
|
// Handle
|
|
|
|
f_sprintf( (char *)&m_ucKey[ 0], "%0*X",
|
|
(int)(sizeof( FLMUINT) * 2),
|
|
(unsigned)((FLMUINT)hDb));
|
|
|
|
// Token
|
|
|
|
f_sprintf( (char *)&m_ucKey[ sizeof( FLMUINT) * 2], "%0*X",
|
|
(int)(sizeof( FLMUINT) * 2),
|
|
(unsigned)m_pSession->getNextToken());
|
|
|
|
m_ucKey[ sizeof( m_ucKey) - 1] = 0;
|
|
return( FERR_OK);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Returns the key and key length of a database object
|
|
****************************************************************************/
|
|
void * F_SessionDb::getKey(
|
|
FLMUINT * puiKeyLen)
|
|
{
|
|
if( puiKeyLen)
|
|
{
|
|
*puiKeyLen = (FLMUINT)sizeof( m_ucKey);
|
|
}
|
|
return( (void *)(&m_ucKey[ 0]));
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
RCODE FlmAllocFileSystem(
|
|
F_FileSystem ** ppFileSystem)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
|
|
flmAssert( ppFileSystem && *ppFileSystem == NULL);
|
|
|
|
if( (*ppFileSystem = f_new F_FileSystemImp) == NULL)
|
|
{
|
|
rc = RC_SET( FERR_MEM);
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
RCODE FlmAllocFileHandle(
|
|
F_FileHdl ** ppFileHandle)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
|
|
flmAssert( ppFileHandle && *ppFileHandle == NULL);
|
|
|
|
if( (*ppFileHandle = f_new F_FileHdlImp) == NULL)
|
|
{
|
|
rc = RC_SET( FERR_MEM);
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Deletes (releases) and F_CCS objected referenced in the ITT table.
|
|
*****************************************************************************/
|
|
void flmDeleteCCSRefs(
|
|
FDICT * pDict
|
|
)
|
|
{
|
|
FLMUINT uiLoop;
|
|
F_CCS * pCcs = NULL;
|
|
ITT * pItt;
|
|
|
|
if (pDict && pDict->pIttTbl)
|
|
{
|
|
for ( pItt = pDict->pIttTbl, uiLoop = 0;
|
|
uiLoop < pDict->uiIttCnt; pItt++, uiLoop++)
|
|
{
|
|
if (ITT_IS_ENCDEF(pItt))
|
|
{
|
|
pCcs = (F_CCS *)pItt->pvItem;
|
|
pItt->pvItem = NULL;
|
|
if (pCcs)
|
|
{
|
|
pCcs->Release();
|
|
pCcs = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|