windows-nt/Source/XPSP1/NT/inetsrv/iis/iisrearc/iisplus/odbc/odbcpool.cxx
2020-09-26 16:20:57 +08:00

461 lines
9.8 KiB
C++

/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
odbcpool.cxx
Abstract:
Provides simple ODBC connection pooling for IDC. The only keys
used for the connection pooling is the datasource name, the
username and password. ODBC options and other connection state
are not taken into consideration.
Author:
John Ludeman (johnl) 01-Apr-1996
Revision History:
--*/
#include "precomp.hxx"
#ifndef _NO_TRACING_
#define IDC_PRINTF( x ) { char buff[256]; wsprintfA x; \
DBGPRINTF((DBG_CONTEXT, buff )); }
#else
#if DBG
#define IDC_PRINTF( x ) { char buff[256]; wsprintfA x; \
OutputDebugString( buff ); }
#else
#define IDC_PRINTF( x )
#endif
#endif
//
// Globals
//
CRITICAL_SECTION g_csPoolLock;
LIST_ENTRY g_PoolList;
DWORD g_dwTimeoutID = 0;
//
// Various counters
//
DWORD g_cFree;
DWORD g_cUsed;
ALLOC_CACHE_HANDLER * ODBC_CONN_POOL::sm_pachOdbcConnPools;
//static
HRESULT
ODBC_CONN_POOL::Initialize(
VOID
)
/*++
Routine Description:
Initialize ODBC_CONN_POOL lookaside
Arguments:
None
Return Value:
HRESULT
--*/
{
ALLOC_CACHE_CONFIGURATION acConfig;
acConfig.nConcurrency = 1;
acConfig.nThreshold = 100;
acConfig.cbSize = sizeof( ODBC_CONN_POOL );
DBG_ASSERT( sm_pachOdbcConnPools == NULL );
sm_pachOdbcConnPools = new ALLOC_CACHE_HANDLER( "ODBC_CONN_POOL",
&acConfig );
if ( sm_pachOdbcConnPools == NULL )
{
return HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
}
return NO_ERROR;
}
//static
VOID
ODBC_CONN_POOL::Terminate(
VOID
)
/*++
Routine Description:
Terminate ODBC_CONN_POOL lookaside
Arguments:
None
Return Value:
None
--*/
{
if ( sm_pachOdbcConnPools != NULL )
{
delete sm_pachOdbcConnPools;
sm_pachOdbcConnPools = NULL;
}
}
VOID
WINAPI
IDCPoolScavenger(
PVOID pContext
);
BOOL
InitializeOdbcPool(
VOID
)
{
DWORD err;
HKEY hkey;
InitializeListHead( &g_PoolList );
INITIALIZE_CRITICAL_SECTION( &g_csPoolLock );
//
// Kick off the pool scavenger
//
g_dwTimeoutID = ScheduleWorkItem( IDCPoolScavenger,
NULL,
IDC_POOL_TIMEOUT * 1000,
TRUE );
return TRUE;
}
VOID
TerminateOdbcPool(
VOID
)
{
ODBC_CONN_POOL * pOCPool;
if ( g_dwTimeoutID )
{
RemoveWorkItem( g_dwTimeoutID );
g_dwTimeoutID = 0;
}
EnterCriticalSection( &g_csPoolLock );
while ( !IsListEmpty( &g_PoolList ))
{
LIST_ENTRY * pEntry = g_PoolList.Flink;
RemoveEntryList( pEntry );
pOCPool = CONTAINING_RECORD( pEntry,
ODBC_CONN_POOL,
m_ListEntry );
delete pOCPool;
pOCPool = NULL;
}
LeaveCriticalSection( &g_csPoolLock );
DeleteCriticalSection( &g_csPoolLock );
}
HRESULT
OpenConnection(
IN ODBC_CONNECTION * podbcconnNonPooled,
OUT ODBC_CONNECTION * * ppodbcconnToUse,
IN DWORD csecPoolTimeout,
IN const CHAR * pszDataSource,
IN const CHAR * pszUsername,
IN const CHAR * pszPassword,
IN const CHAR * pszLoggedOnUser
)
/*++
Routine Description:
This function opens an odbc connection, optionally from a pool of
ODBC connections.
Arguments:
podbcconnNonPooled - If pooling wasn't requested or the open
failed, we use this odbc connection object
ppodbcconnToUse - Receives pointer to either a pooled ODBC
connection object or podbcconnNonPooled if a
pooled object couldn't be used
csecPoolTimeout - Amount of time to pool a connection, 0 to not
pool
pszDataSource - ODBC Datasource
pszUsername - Username for datasource access
pszPassword - Password for use with this username
pszLoggedOnUser - The NT account this user is running under
Return Value:
HRESULT
ppodbcconnToUse will be set to the ODBC connection to use for the
request
--*/
{
LIST_ENTRY * pEntry;
ODBC_CONN_POOL * pOCPool;
HRESULT hr;
//
// Don't pool this connection if it wasn't requested
//
if ( !csecPoolTimeout )
{
*ppodbcconnToUse = podbcconnNonPooled;
return podbcconnNonPooled->Open( pszDataSource,
pszUsername,
pszPassword );
}
//
// Look in the pool cache for an existing connection
//
EnterCriticalSection( &g_csPoolLock );
for ( pEntry = g_PoolList.Flink;
pEntry != &g_PoolList;
pEntry = pEntry->Flink )
{
pOCPool = CONTAINING_RECORD( pEntry,
ODBC_CONN_POOL,
m_ListEntry );
if ( pOCPool->IsFree() &&
!lstrcmpiA( pOCPool->QueryDataSource(),
pszDataSource ) &&
!lstrcmpiA( pOCPool->QueryUsername(),
pszUsername ) &&
!lstrcmpiA( pOCPool->QueryLoggedOnUser(),
pszLoggedOnUser ) &&
!strcmp( pOCPool->QueryPassword(),
pszPassword ))
{
//
// We have a match
//
pOCPool->MarkAsUsed();
*ppodbcconnToUse = pOCPool->QueryOdbcConnection();
pOCPool->SetTTL( csecPoolTimeout );
LeaveCriticalSection( &g_csPoolLock );
return S_OK;
}
}
LeaveCriticalSection( &g_csPoolLock );
//
// Allocate a new connection pool and if we connect successfully,
// put it in the pool list
//
pOCPool = new ODBC_CONN_POOL();
if( pOCPool == NULL )
{
return HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
}
hr = pOCPool->Create( pszDataSource,
pszUsername,
pszPassword,
pszLoggedOnUser );
if( FAILED( hr ) )
{
DBGPRINTF(( DBG_CONTEXT,
"Error creating pool connection, hr = 0x%x.\n",
hr ));
return hr;
}
if ( FAILED( pOCPool->Open() ) )
{
delete pOCPool;
pOCPool = NULL;
*ppodbcconnToUse = podbcconnNonPooled;
return podbcconnNonPooled->Open( pszDataSource,
pszUsername,
pszPassword );
}
*ppodbcconnToUse = pOCPool->QueryOdbcConnection();
EnterCriticalSection( &g_csPoolLock );
//
// Account for the new pool item but we have to do it
// with in the critical section
//
g_cFree++;
pOCPool->MarkAsUsed();
pOCPool->SetTTL( csecPoolTimeout );
InsertHeadList( &g_PoolList, &pOCPool->m_ListEntry );
LeaveCriticalSection( &g_csPoolLock );
return S_OK;
}
VOID
CloseConnection(
IN ODBC_CONNECTION * podbcconnPooled,
IN BOOL fDelete
)
/*++
Routine Description:
This routine frees an ODBC connection back to the pool,
optionally deleting it
Arguments:
podbcconnPooled - ODBC connection that is pooled, can be NULL
fDelete - TRUE if the item should be delete rather then returned
to the pool
--*/
{
LIST_ENTRY * pEntry;
ODBC_CONN_POOL * pOCPool;
if ( !podbcconnPooled )
{
return;
}
//
// Look in the pool list to mark it as free
//
EnterCriticalSection( &g_csPoolLock );
for ( pEntry = g_PoolList.Flink;
pEntry != &g_PoolList;
pEntry = pEntry->Flink )
{
pOCPool = CONTAINING_RECORD( pEntry,
ODBC_CONN_POOL,
m_ListEntry );
if ( podbcconnPooled == pOCPool->QueryOdbcConnection() )
{
pOCPool->MarkAsFree();
if ( fDelete )
{
RemoveEntryList( pEntry );
g_cFree--;
delete pOCPool;
pOCPool = NULL;
}
break;
}
}
LeaveCriticalSection( &g_csPoolLock );
}
VOID
WINAPI
IDCPoolScavenger(
PVOID pContext
)
/*++
Routine Description:
Walks the list of pooled connections and removes any that have
timed out
--*/
{
LIST_ENTRY * pEntry;
LIST_ENTRY * pNext;
ODBC_CONN_POOL * pOCPool;
//
// Look through the list and remove any old items
//
EnterCriticalSection( &g_csPoolLock );
for ( pEntry = g_PoolList.Flink;
pEntry != &g_PoolList;
pEntry = pNext )
{
pNext = pEntry->Flink;
pOCPool = CONTAINING_RECORD( pEntry,
ODBC_CONN_POOL,
m_ListEntry );
if ( pOCPool->IsFree() && !pOCPool->DecrementTTL() )
{
IDC_PRINTF(( buff,
"[IDCPoolScavenger] Removing %s, %s, %s\n",
pOCPool->QueryDataSource(),
pOCPool->QueryUsername(),
pOCPool->QueryLoggedOnUser() ));
RemoveEntryList( pEntry );
g_cFree--;
delete pOCPool;
pOCPool = NULL;
}
}
#if 0
IDC_PRINTF(( buff,
"[IDCPoolScavenger] Free items in pool %d, used %d\n",
g_cFree,
g_cUsed ));
#endif
LeaveCriticalSection( &g_csPoolLock );
}