Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

460 lines
9.8 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. odbcpool.cxx
  5. Abstract:
  6. Provides simple ODBC connection pooling for IDC. The only keys
  7. used for the connection pooling is the datasource name, the
  8. username and password. ODBC options and other connection state
  9. are not taken into consideration.
  10. Author:
  11. John Ludeman (johnl) 01-Apr-1996
  12. Revision History:
  13. --*/
  14. #include "precomp.hxx"
  15. #ifndef _NO_TRACING_
  16. #define IDC_PRINTF( x ) { char buff[256]; wsprintfA x; \
  17. DBGPRINTF((DBG_CONTEXT, buff )); }
  18. #else
  19. #if DBG
  20. #define IDC_PRINTF( x ) { char buff[256]; wsprintfA x; \
  21. OutputDebugString( buff ); }
  22. #else
  23. #define IDC_PRINTF( x )
  24. #endif
  25. #endif
  26. //
  27. // Globals
  28. //
  29. CRITICAL_SECTION g_csPoolLock;
  30. LIST_ENTRY g_PoolList;
  31. DWORD g_dwTimeoutID = 0;
  32. //
  33. // Various counters
  34. //
  35. DWORD g_cFree;
  36. DWORD g_cUsed;
  37. ALLOC_CACHE_HANDLER * ODBC_CONN_POOL::sm_pachOdbcConnPools;
  38. //static
  39. HRESULT
  40. ODBC_CONN_POOL::Initialize(
  41. VOID
  42. )
  43. /*++
  44. Routine Description:
  45. Initialize ODBC_CONN_POOL lookaside
  46. Arguments:
  47. None
  48. Return Value:
  49. HRESULT
  50. --*/
  51. {
  52. ALLOC_CACHE_CONFIGURATION acConfig;
  53. acConfig.nConcurrency = 1;
  54. acConfig.nThreshold = 100;
  55. acConfig.cbSize = sizeof( ODBC_CONN_POOL );
  56. DBG_ASSERT( sm_pachOdbcConnPools == NULL );
  57. sm_pachOdbcConnPools = new ALLOC_CACHE_HANDLER( "ODBC_CONN_POOL",
  58. &acConfig );
  59. if ( sm_pachOdbcConnPools == NULL )
  60. {
  61. return HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
  62. }
  63. return NO_ERROR;
  64. }
  65. //static
  66. VOID
  67. ODBC_CONN_POOL::Terminate(
  68. VOID
  69. )
  70. /*++
  71. Routine Description:
  72. Terminate ODBC_CONN_POOL lookaside
  73. Arguments:
  74. None
  75. Return Value:
  76. None
  77. --*/
  78. {
  79. if ( sm_pachOdbcConnPools != NULL )
  80. {
  81. delete sm_pachOdbcConnPools;
  82. sm_pachOdbcConnPools = NULL;
  83. }
  84. }
  85. VOID
  86. WINAPI
  87. IDCPoolScavenger(
  88. PVOID pContext
  89. );
  90. BOOL
  91. InitializeOdbcPool(
  92. VOID
  93. )
  94. {
  95. DWORD err;
  96. HKEY hkey;
  97. InitializeListHead( &g_PoolList );
  98. INITIALIZE_CRITICAL_SECTION( &g_csPoolLock );
  99. //
  100. // Kick off the pool scavenger
  101. //
  102. g_dwTimeoutID = ScheduleWorkItem( IDCPoolScavenger,
  103. NULL,
  104. IDC_POOL_TIMEOUT * 1000,
  105. TRUE );
  106. return TRUE;
  107. }
  108. VOID
  109. TerminateOdbcPool(
  110. VOID
  111. )
  112. {
  113. ODBC_CONN_POOL * pOCPool;
  114. if ( g_dwTimeoutID )
  115. {
  116. RemoveWorkItem( g_dwTimeoutID );
  117. g_dwTimeoutID = 0;
  118. }
  119. EnterCriticalSection( &g_csPoolLock );
  120. while ( !IsListEmpty( &g_PoolList ))
  121. {
  122. LIST_ENTRY * pEntry = g_PoolList.Flink;
  123. RemoveEntryList( pEntry );
  124. pOCPool = CONTAINING_RECORD( pEntry,
  125. ODBC_CONN_POOL,
  126. m_ListEntry );
  127. delete pOCPool;
  128. pOCPool = NULL;
  129. }
  130. LeaveCriticalSection( &g_csPoolLock );
  131. DeleteCriticalSection( &g_csPoolLock );
  132. }
  133. HRESULT
  134. OpenConnection(
  135. IN ODBC_CONNECTION * podbcconnNonPooled,
  136. OUT ODBC_CONNECTION * * ppodbcconnToUse,
  137. IN DWORD csecPoolTimeout,
  138. IN const CHAR * pszDataSource,
  139. IN const CHAR * pszUsername,
  140. IN const CHAR * pszPassword,
  141. IN const CHAR * pszLoggedOnUser
  142. )
  143. /*++
  144. Routine Description:
  145. This function opens an odbc connection, optionally from a pool of
  146. ODBC connections.
  147. Arguments:
  148. podbcconnNonPooled - If pooling wasn't requested or the open
  149. failed, we use this odbc connection object
  150. ppodbcconnToUse - Receives pointer to either a pooled ODBC
  151. connection object or podbcconnNonPooled if a
  152. pooled object couldn't be used
  153. csecPoolTimeout - Amount of time to pool a connection, 0 to not
  154. pool
  155. pszDataSource - ODBC Datasource
  156. pszUsername - Username for datasource access
  157. pszPassword - Password for use with this username
  158. pszLoggedOnUser - The NT account this user is running under
  159. Return Value:
  160. HRESULT
  161. ppodbcconnToUse will be set to the ODBC connection to use for the
  162. request
  163. --*/
  164. {
  165. LIST_ENTRY * pEntry;
  166. ODBC_CONN_POOL * pOCPool;
  167. HRESULT hr;
  168. //
  169. // Don't pool this connection if it wasn't requested
  170. //
  171. if ( !csecPoolTimeout )
  172. {
  173. *ppodbcconnToUse = podbcconnNonPooled;
  174. return podbcconnNonPooled->Open( pszDataSource,
  175. pszUsername,
  176. pszPassword );
  177. }
  178. //
  179. // Look in the pool cache for an existing connection
  180. //
  181. EnterCriticalSection( &g_csPoolLock );
  182. for ( pEntry = g_PoolList.Flink;
  183. pEntry != &g_PoolList;
  184. pEntry = pEntry->Flink )
  185. {
  186. pOCPool = CONTAINING_RECORD( pEntry,
  187. ODBC_CONN_POOL,
  188. m_ListEntry );
  189. if ( pOCPool->IsFree() &&
  190. !lstrcmpiA( pOCPool->QueryDataSource(),
  191. pszDataSource ) &&
  192. !lstrcmpiA( pOCPool->QueryUsername(),
  193. pszUsername ) &&
  194. !lstrcmpiA( pOCPool->QueryLoggedOnUser(),
  195. pszLoggedOnUser ) &&
  196. !strcmp( pOCPool->QueryPassword(),
  197. pszPassword ))
  198. {
  199. //
  200. // We have a match
  201. //
  202. pOCPool->MarkAsUsed();
  203. *ppodbcconnToUse = pOCPool->QueryOdbcConnection();
  204. pOCPool->SetTTL( csecPoolTimeout );
  205. LeaveCriticalSection( &g_csPoolLock );
  206. return S_OK;
  207. }
  208. }
  209. LeaveCriticalSection( &g_csPoolLock );
  210. //
  211. // Allocate a new connection pool and if we connect successfully,
  212. // put it in the pool list
  213. //
  214. pOCPool = new ODBC_CONN_POOL();
  215. if( pOCPool == NULL )
  216. {
  217. return HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
  218. }
  219. hr = pOCPool->Create( pszDataSource,
  220. pszUsername,
  221. pszPassword,
  222. pszLoggedOnUser );
  223. if( FAILED( hr ) )
  224. {
  225. DBGPRINTF(( DBG_CONTEXT,
  226. "Error creating pool connection, hr = 0x%x.\n",
  227. hr ));
  228. return hr;
  229. }
  230. if ( FAILED( pOCPool->Open() ) )
  231. {
  232. delete pOCPool;
  233. pOCPool = NULL;
  234. *ppodbcconnToUse = podbcconnNonPooled;
  235. return podbcconnNonPooled->Open( pszDataSource,
  236. pszUsername,
  237. pszPassword );
  238. }
  239. *ppodbcconnToUse = pOCPool->QueryOdbcConnection();
  240. EnterCriticalSection( &g_csPoolLock );
  241. //
  242. // Account for the new pool item but we have to do it
  243. // with in the critical section
  244. //
  245. g_cFree++;
  246. pOCPool->MarkAsUsed();
  247. pOCPool->SetTTL( csecPoolTimeout );
  248. InsertHeadList( &g_PoolList, &pOCPool->m_ListEntry );
  249. LeaveCriticalSection( &g_csPoolLock );
  250. return S_OK;
  251. }
  252. VOID
  253. CloseConnection(
  254. IN ODBC_CONNECTION * podbcconnPooled,
  255. IN BOOL fDelete
  256. )
  257. /*++
  258. Routine Description:
  259. This routine frees an ODBC connection back to the pool,
  260. optionally deleting it
  261. Arguments:
  262. podbcconnPooled - ODBC connection that is pooled, can be NULL
  263. fDelete - TRUE if the item should be delete rather then returned
  264. to the pool
  265. --*/
  266. {
  267. LIST_ENTRY * pEntry;
  268. ODBC_CONN_POOL * pOCPool;
  269. if ( !podbcconnPooled )
  270. {
  271. return;
  272. }
  273. //
  274. // Look in the pool list to mark it as free
  275. //
  276. EnterCriticalSection( &g_csPoolLock );
  277. for ( pEntry = g_PoolList.Flink;
  278. pEntry != &g_PoolList;
  279. pEntry = pEntry->Flink )
  280. {
  281. pOCPool = CONTAINING_RECORD( pEntry,
  282. ODBC_CONN_POOL,
  283. m_ListEntry );
  284. if ( podbcconnPooled == pOCPool->QueryOdbcConnection() )
  285. {
  286. pOCPool->MarkAsFree();
  287. if ( fDelete )
  288. {
  289. RemoveEntryList( pEntry );
  290. g_cFree--;
  291. delete pOCPool;
  292. pOCPool = NULL;
  293. }
  294. break;
  295. }
  296. }
  297. LeaveCriticalSection( &g_csPoolLock );
  298. }
  299. VOID
  300. WINAPI
  301. IDCPoolScavenger(
  302. PVOID pContext
  303. )
  304. /*++
  305. Routine Description:
  306. Walks the list of pooled connections and removes any that have
  307. timed out
  308. --*/
  309. {
  310. LIST_ENTRY * pEntry;
  311. LIST_ENTRY * pNext;
  312. ODBC_CONN_POOL * pOCPool;
  313. //
  314. // Look through the list and remove any old items
  315. //
  316. EnterCriticalSection( &g_csPoolLock );
  317. for ( pEntry = g_PoolList.Flink;
  318. pEntry != &g_PoolList;
  319. pEntry = pNext )
  320. {
  321. pNext = pEntry->Flink;
  322. pOCPool = CONTAINING_RECORD( pEntry,
  323. ODBC_CONN_POOL,
  324. m_ListEntry );
  325. if ( pOCPool->IsFree() && !pOCPool->DecrementTTL() )
  326. {
  327. IDC_PRINTF(( buff,
  328. "[IDCPoolScavenger] Removing %s, %s, %s\n",
  329. pOCPool->QueryDataSource(),
  330. pOCPool->QueryUsername(),
  331. pOCPool->QueryLoggedOnUser() ));
  332. RemoveEntryList( pEntry );
  333. g_cFree--;
  334. delete pOCPool;
  335. pOCPool = NULL;
  336. }
  337. }
  338. #if 0
  339. IDC_PRINTF(( buff,
  340. "[IDCPoolScavenger] Free items in pool %d, used %d\n",
  341. g_cFree,
  342. g_cUsed ));
  343. #endif
  344. LeaveCriticalSection( &g_csPoolLock );
  345. }