Leaked source code of windows server 2003
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.

594 lines
19 KiB

  1. #ifndef __OBJPOOL_INC
  2. #define __OBJPOOL_INC
  3. // --------------------------------------------------------------------------------
  4. // ObjPool.h
  5. // Copyright (c)1998 Microsoft Corporation, All Rights Reserved
  6. // Don Dumitru ([email protected])
  7. // --------------------------------------------------------------------------------
  8. /*
  9. Typical usage of this set of templates is like this:
  10. Say you want to provide a pool of structs of type PROPERTY. You
  11. would first define the allocator class that you want to use for
  12. allocating those structs - if you wanted to use operator new and
  13. operator delete, you would do this...
  14. typedef CAllocObjWithNew<PROPERTY> PROPERTY_ALLOCATOR;
  15. Now, you can define a pool of PROPERTY structs, using that allocator
  16. class, by doing this...
  17. typedef CAutoObjPool<PROPERTY,offsetof(PROPERTY,pNext),PROPERTY_ALLOCATOR> PROPERTY_POOL;
  18. and then declare instances of the PROPERTY pool by doing this...
  19. PROPERTY_POOL g_PropPool;
  20. If you want to use a different allocator, just change the definition of
  21. the PROPERTY_ALLOCATOR typedef - templates for using operator new and
  22. operator delete, or for using an instance of interface IMalloc are
  23. provided. Or you can implement your own allocator class using
  24. whatever mechanism you want.
  25. If for some reason you can't use the provided auto-allocator mechanism
  26. in CAutoObjPool<> (maybe you need to pass parameters to the constructor
  27. or somesuch - although you might be able to solve that by how you define
  28. the allocator class...), you can implement your own version of
  29. CAutoObjPool<> which does it the way you want.
  30. One feature of this set of templates is the ability to adjust the size
  31. of the pool. This would let you, for example, adjust the pool size up
  32. as container objects were allocated, and adjust it back down as those
  33. container objects were freed. This would let the pool be sized based
  34. on how many container objects were being held by the application - so
  35. the overall caching strategy of the subsystem would be responsive to
  36. how the top-level objects were being cached by the application.
  37. */
  38. //#include <stdarg.h>
  39. // Since InterlockedExchangeAdd is not available on Win95, we'll just
  40. // disable all of this stats stuff...
  41. #if 0
  42. class CPoolStatsDebug {
  43. public:
  44. CPoolStatsDebug() {
  45. m_lTotalAllocs = 0;
  46. m_lFailedAllocs = 0;
  47. m_lTotalAllocOverhead = 0;
  48. m_lTotalFrees = 0;
  49. m_lFailedFrees = 0;
  50. m_lTotalFreeOverhead = 0;
  51. };
  52. void AllocSuccess(DWORD dwOverhead) {
  53. InterlockedIncrement(&m_lTotalAllocs);
  54. InterlockedExchangeAdd(&m_lTotalAllocOverhead,dwOverhead);
  55. };
  56. void AllocFail() {
  57. InterlockedIncrement(&m_lTotalAllocs);
  58. InterlockedIncrement(&m_lFailedAllocs);
  59. };
  60. void FreeSuccess(DWORD dwOverhead) {
  61. InterlockedIncrement(&m_lTotalFrees);
  62. InterlockedExchangeAdd(&m_lTotalFreeOverhead,dwOverhead);
  63. };
  64. void FreeFail() {
  65. InterlockedIncrement(&m_lTotalFrees);
  66. InterlockedIncrement(&m_lFailedFrees);
  67. };
  68. void Dump() {
  69. DbgPrintF("Object Pool Stats:\r\n");
  70. DbgPrintF(" Total Allocs - %u\r\n",m_lTotalAllocs);
  71. DbgPrintF(" Failed Allocs - %u\r\n",m_lFailedAllocs);
  72. DbgPrintF(" Total Alloc Overhead - %u\r\n",m_lTotalAllocOverhead);
  73. if (m_lTotalAllocs-m_lFailedAllocs) {
  74. DbgPrintF(" Average Alloc Overhead - %u\r\n",m_lTotalAllocOverhead/(m_lTotalAllocs-m_lFailedAllocs));
  75. }
  76. DbgPrintF(" Total Frees - %u\r\n",m_lTotalFrees);
  77. DbgPrintF(" Failed Frees - %u\r\n",m_lFailedFrees);
  78. DbgPrintF(" Total Free Overhead - %u\r\n",m_lTotalFreeOverhead);
  79. if (m_lTotalFrees-m_lFailedFrees) {
  80. DbgPrintF(" Average Free Overhead - %u\r\n",m_lTotalFreeOverhead/(m_lTotalFrees-m_lFailedFrees));
  81. }
  82. };
  83. private:
  84. static void DbgPrintF(LPCSTR pszFmt, ...)
  85. {
  86. char szOutput[512];
  87. va_list val;
  88. va_start(val,pszFmt);
  89. wvnsprintfA(szOutput, ARRAYSIZE(szOutput), pszFmt,val);
  90. va_end(val);
  91. OutputDebugStringA(szOutput);
  92. };
  93. long m_lTotalAllocs;
  94. long m_lFailedAllocs;
  95. long m_lTotalAllocOverhead;
  96. long m_lTotalFrees;
  97. long m_lFailedFrees;
  98. long m_lTotalFreeOverhead;
  99. };
  100. #endif
  101. class CPoolStatsRetail {
  102. public:
  103. static void AllocSuccess(DWORD) {
  104. };
  105. static void AllocFail() {
  106. };
  107. static void FreeSuccess(DWORD) {
  108. };
  109. static void FreeFail() {
  110. };
  111. static void Dump() {
  112. };
  113. };
  114. #if 0
  115. #ifdef DEBUG
  116. typedef CPoolStatsDebug CPoolStats;
  117. #else
  118. typedef CPoolStatsRetail CPoolStats;
  119. #endif
  120. #else
  121. typedef CPoolStatsRetail CPoolStats;
  122. #endif
  123. // This class provides the base implementation of the object pool operations.
  124. // Basically, this class implements everything that doesn't depend in any way
  125. // on the type of the object being pooled. By doing a base class for all of
  126. // these methods, we minimize the number of instances of these methods which
  127. // will be generated by the compiler (since these particular methods are not
  128. // dependent on the template parameters, we only need to have one version of
  129. // them).
  130. //
  131. // This class' locking is based on critical sections. On multi-proc machines
  132. // you can get much better contention behavior by keeping the length of time
  133. // that you hold critical sections at an absolute minimum, and by using the
  134. // InitializeCriticalSectionAndSpinCount function (provided by NT4.sp3). This
  135. // class does this - and since the function is not available on older versions
  136. // of NT, or on Win95, this class uses LoadLibrary/GetProcAddress to attempt
  137. // to call the new function, and it falls back on InitializeCriticalSection
  138. // if the new function is not available.
  139. class CObjPoolImplBase : public CPoolStats
  140. {
  141. private:
  142. typedef BOOL (WINAPI *PFN_ICSASC)(LPCRITICAL_SECTION,DWORD);
  143. public:
  144. void Init(DWORD dwMax)
  145. {
  146. HMODULE hmod;
  147. PFN_ICSASC pfn = NULL;
  148. m_pvList = NULL;
  149. m_lCount = 0;
  150. m_lMax = dwMax;
  151. // WARNING! This Init() method might be called from within
  152. // DllEntryPoint - which means that calling LoadLibrary is
  153. // a Bad Idea. But, we know that kernel32.dll is always
  154. // loaded already, so it happens to be safe in this one
  155. // instance...
  156. hmod = LoadLibrary("kernel32.dll");
  157. if (hmod)
  158. {
  159. pfn = (PFN_ICSASC) GetProcAddress(hmod,"InitializeCriticalSectionAndSpinCount");
  160. }
  161. if (!pfn || !pfn(&m_cs,4000))
  162. {
  163. // Either we didn't get the function pointer, or
  164. // the function failed - either way, fall-back to
  165. // using a normal critsec.
  166. InitializeCriticalSection(&m_cs);
  167. }
  168. if (hmod)
  169. {
  170. FreeLibrary(hmod);
  171. }
  172. };
  173. LPVOID Term()
  174. {
  175. LPVOID pvResult;
  176. Assert(m_lCount>=0);
  177. EnterCriticalSection(&m_cs);
  178. pvResult = m_pvList;
  179. m_pvList = NULL;
  180. m_lCount = 0;
  181. m_lMax = 0;
  182. LeaveCriticalSection(&m_cs);
  183. DeleteCriticalSection(&m_cs);
  184. Dump();
  185. return (pvResult);
  186. };
  187. void GrowPool(DWORD dwGrowBy)
  188. {
  189. EnterCriticalSection(&m_cs);
  190. m_lMax += dwGrowBy;
  191. LeaveCriticalSection(&m_cs);
  192. Assert(m_lMax>=0);
  193. };
  194. LPVOID GetAll() {
  195. LPVOID pvObject;
  196. EnterCriticalSection(&m_cs);
  197. pvObject = m_pvList;
  198. m_pvList = NULL;
  199. m_lCount = 0;
  200. LeaveCriticalSection(&m_cs);
  201. return (pvObject);
  202. };
  203. protected:
  204. volatile LPVOID m_pvList;
  205. volatile LONG m_lCount;
  206. volatile LONG m_lMax;
  207. CRITICAL_SECTION m_cs;
  208. };
  209. // This template class inherits from the base class, and adds all of the
  210. // things which depend on the offset of the "next" field in the objects
  211. // being pooled. The compiler will instantiate a version of this template
  212. // class for each object pool which has the "next" field at a different
  213. // offset within the pooled objects.
  214. template <DWORD dwNextLinkOffset>
  215. class CObjPoolImpl : public CObjPoolImplBase
  216. {
  217. public:
  218. LPVOID GetFromPool()
  219. {
  220. LPVOID pvResult = NULL;
  221. Assert(m_lCount>=0);
  222. EnterCriticalSection(&m_cs);
  223. Assert(!m_pvList||(m_lCount>0));
  224. if (m_pvList)
  225. {
  226. pvResult = m_pvList;
  227. m_pvList = *off(m_pvList);
  228. *off(pvResult) = NULL;
  229. m_lCount--;
  230. AllocSuccess(m_lMax-m_lCount);
  231. } else {
  232. AllocFail();
  233. }
  234. LeaveCriticalSection(&m_cs);
  235. return (pvResult);
  236. };
  237. BOOL AddToPool(LPVOID pvObject)
  238. {
  239. BOOL bResult = FALSE;
  240. Assert((m_lCount>=0)&&(m_lCount<=m_lMax));
  241. EnterCriticalSection(&m_cs);
  242. if (m_lCount < m_lMax)
  243. {
  244. *off(pvObject) = m_pvList;
  245. m_pvList = pvObject;
  246. m_lCount++;
  247. bResult = TRUE;
  248. FreeSuccess(m_lMax-m_lCount);
  249. } else {
  250. FreeFail();
  251. }
  252. LeaveCriticalSection(&m_cs);
  253. return (bResult);
  254. };
  255. LPVOID ShrinkPool(DWORD dwShrinkBy)
  256. {
  257. LPVOID pvResult = NULL;
  258. DWORD dwCount;
  259. Assert((m_lCount>=0)&&(m_lCount<=m_lMax));
  260. Assert((DWORD) m_lMax>=dwShrinkBy);
  261. EnterCriticalSection(&m_cs);
  262. m_lMax -= dwShrinkBy;
  263. while (m_lCount > m_lMax)
  264. {
  265. LPVOID pvTmp;
  266. pvTmp = m_pvList;
  267. m_pvList = *off(m_pvList);
  268. *off(pvTmp) = pvResult;
  269. pvResult = pvTmp;
  270. m_lCount--;
  271. }
  272. LeaveCriticalSection(&m_cs);
  273. return (pvResult);
  274. };
  275. static LPVOID *off(LPVOID pvObject)
  276. {
  277. return ((LPVOID *) (((LPBYTE) pvObject)+dwNextLinkOffset));
  278. };
  279. };
  280. // This template class just wraps the CObjPoolImpl<> template class
  281. // with type conversion for the actual object type being pooled - it
  282. // hides the fact that the underlying implementation is dealing with
  283. // void pointers. The compiler will instantiate a version of this
  284. // template for each different type of object being pooled. However,
  285. // since all of the methods of this template class are doing trivial
  286. // type casting, this template class shouldn't actually cause any code
  287. // to be generated.
  288. template <class T, DWORD dwNextLinkOffset, class A>
  289. class CObjPool : public CObjPoolImpl<dwNextLinkOffset>
  290. {
  291. private:
  292. typedef CObjPoolImpl<dwNextLinkOffset> O;
  293. public:
  294. T *Term()
  295. {
  296. return ((T *) O::Term());
  297. };
  298. T *GetFromPool()
  299. {
  300. return ((T *) O::GetFromPool());
  301. };
  302. BOOL AddToPool(T *pObject)
  303. {
  304. A::CleanObject(pObject);
  305. return (O::AddToPool(pObject));
  306. };
  307. void GrowPool(DWORD dwGrowBy)
  308. {
  309. O::GrowPool(dwGrowBy);
  310. };
  311. T *ShrinkPool(DWORD dwShrinkBy)
  312. {
  313. return ((T *) O::ShrinkPool(dwShrinkBy));
  314. };
  315. T *GetAll() {
  316. return ((T *) O::GetAll());
  317. };
  318. static T **off(T *pObject)
  319. {
  320. return ((T ** ) O::off(pObject));
  321. };
  322. };
  323. // This template class provides multi-proc scalability for
  324. // a pool, by creating up to eight sub-pools - the actual
  325. // number of sub-pools is equal to the number of processors
  326. // on the machine, and the objects are evenly distributed
  327. // between the sub-pools.
  328. template <class T, DWORD dwNextLinkOffset, class O>
  329. class CObjPoolMultiBase
  330. {
  331. public:
  332. void Init(DWORD dwMax)
  333. {
  334. SYSTEM_INFO siInfo;
  335. DWORD dwIdx;
  336. m_dwNext = 0;
  337. GetSystemInfo(&siInfo);
  338. m_dwMax = siInfo.dwNumberOfProcessors;
  339. if (m_dwMax < 1) {
  340. m_dwMax = 1;
  341. }
  342. if (m_dwMax > sizeof(m_abPool)/sizeof(m_abPool[0])) {
  343. m_dwMax = sizeof(m_abPool)/sizeof(m_abPool[0]);
  344. }
  345. for (dwIdx=0;dwIdx<m_dwMax;dwIdx++) {
  346. m_abPool[dwIdx].Init(Calc(dwMax));
  347. }
  348. };
  349. T *Term() {
  350. T *pObject = NULL;
  351. T **ppLast = &pObject;
  352. DWORD dwIdx;
  353. for (dwIdx=0;dwIdx<m_dwMax;dwIdx++) {
  354. *ppLast = m_abPool[dwIdx].Term();
  355. while (*ppLast) {
  356. ppLast = off(*ppLast);
  357. }
  358. }
  359. return (pObject);
  360. };
  361. T *GetFromPool() {
  362. return (m_abPool[PickNext()].GetFromPool());
  363. };
  364. BOOL AddToPool(T* pObject) {
  365. return (m_abPool[PickNext()].AddToPool(pObject));
  366. };
  367. void GrowPool(DWORD dwGrowBy) {
  368. for (DWORD dwIdx=0;dwIdx<m_dwMax;dwIdx++) {
  369. m_abPool[dwIdx].GrowPool(Calc(dwGrowBy));
  370. }
  371. };
  372. T *ShrinkPool(DWORD dwShrinkBy) {
  373. T *pObject = NULL;
  374. T **ppLast = &pObject;
  375. DWORD dwIdx;
  376. for (dwIdx=0;dwIdx<m_dwMax;dwIdx++) {
  377. *ppLast = m_abPool[dwIdx].ShrinkPool(Calc(dwGrowBy));
  378. while (*ppLast) {
  379. ppLast = off(*ppLast);
  380. }
  381. }
  382. return (pObject);
  383. };
  384. T *GetAll() {
  385. T *pObject = NULL;
  386. T **ppLast = &pObject;
  387. DWORD dwIdx;
  388. for (dwIdx=0;dwIdx<m_dwMax;dwIdx++) {
  389. *ppLast = m_abPool[dwIdx].GetAll();
  390. while (*ppLast) {
  391. ppLast = off(*ppLast);
  392. }
  393. }
  394. return (pObject);
  395. };
  396. static T **off(T* pObject) {
  397. return (O::off(pObject));
  398. };
  399. private:
  400. DWORD Calc(DWORD dwInput) {
  401. return ((dwInput+m_dwMax-1)/m_dwMax);
  402. };
  403. DWORD PickNext() {
  404. return (((DWORD) InterlockedIncrement((LONG *) &m_dwNext)) % m_dwMax);
  405. };
  406. O m_abPool[8];
  407. DWORD m_dwMax;
  408. DWORD m_dwNext;
  409. };
  410. // This template class implements a pool object with automatic
  411. // allocation and freeing of objects. It does this by taking
  412. // an "allocator" class as a template parameter, and using
  413. // methods on that allocator class to perform the allocation
  414. // and freeing of the objects.
  415. template <class T, DWORD dwNextLinkOffset, class A>
  416. class CAutoObjPool : public CObjPool<T,dwNextLinkOffset,A>
  417. {
  418. private:
  419. typedef CObjPool<T,dwNextLinkOffset,A> O;
  420. public:
  421. T *Term()
  422. {
  423. A::FreeList(O::Term());
  424. return (NULL);
  425. };
  426. T *GetFromPool()
  427. {
  428. T *pObject = O::GetFromPool();
  429. if (!pObject)
  430. {
  431. pObject = A::AllocObject();
  432. }
  433. return (pObject);
  434. };
  435. BOOL AddToPool(T *pObject)
  436. {
  437. if (!O::AddToPool(pObject))
  438. {
  439. A::FreeObject(pObject);
  440. }
  441. return (TRUE);
  442. };
  443. T *ShrinkPool(DWORD dwShrinkBy)
  444. {
  445. A::FreeList(O::ShrinkPool(dwShrinkBy));
  446. return (NULL);
  447. };
  448. };
  449. // This template class provides a multi-proc pool without automatic allocation.
  450. template <class T, DWORD dwNextLinkOffset, class A>
  451. class CObjPoolMulti : public CObjPoolMultiBase<T,dwNextLinkOffset,CObjPool<T,dwNextLinkOffset,A> >
  452. {
  453. // nothing
  454. };
  455. // The template class provides a multi-proc pool with automatic allocation.
  456. template <class T, DWORD dwNextLinkOffset, class A>
  457. class CAutoObjPoolMulti : public CObjPoolMultiBase<T,dwNextLinkOffset,CAutoObjPool<T,dwNextLinkOffset,A> >
  458. {
  459. // nothing
  460. };
  461. // This template class provides a base implementation for allocator classes.
  462. template <class T, DWORD dwNextLinkOffset>
  463. class CAllocObjBase
  464. {
  465. public:
  466. static void InitObject(T *pObject) {
  467. memset(pObject,0,sizeof(*pObject));
  468. };
  469. static void CleanObject(T *pObject) {
  470. memset(pObject,0,sizeof(*pObject));
  471. };
  472. static void TermObject(T *pObject) {
  473. // memset(pObject,0xfe,sizeof(*pObject));
  474. };
  475. static T **Off(T *pObject) {
  476. return (T **) (((LPBYTE) pObject)+dwNextLinkOffset);
  477. };
  478. };
  479. // This template class provides a base implementation for allocator classes
  480. // which use new and delete.
  481. template <class T, class O>
  482. class CAllocObjWithNewBase
  483. {
  484. public:
  485. static T *AllocObject()
  486. {
  487. T *pObject = new T;
  488. if (pObject) {
  489. O::InitObject(pObject);
  490. }
  491. return (pObject);
  492. };
  493. static void FreeObject(T *pObject)
  494. {
  495. if (pObject) {
  496. O::TermObject(pObject);
  497. }
  498. delete pObject;
  499. };
  500. static void FreeList(T *pObject) {
  501. while (pObject) {
  502. T *pNext;
  503. pNext = *O::Off(pObject);
  504. FreeObject(pObject);
  505. pObject = pNext;
  506. }
  507. };
  508. };
  509. // This template class provides an allocator using operator
  510. // new and operator delete.
  511. template <class T, DWORD dwNextLinkOffset>
  512. class CAllocObjWithNew :
  513. public CAllocObjBase<T,dwNextLinkOffset>,
  514. public CAllocObjWithNewBase<T,CAllocObjBase<T,dwNextLinkOffset> >
  515. {
  516. // nothing
  517. };
  518. // This template class provides a base implementation for allocator classes
  519. // which use an instance of IMalloc.
  520. template <class T, class O>
  521. class CAllocObjWithIMallocBase
  522. {
  523. public:
  524. static T *AllocObject()
  525. {
  526. T *pObject = (T *) g_pMalloc->Alloc(sizeof(T));
  527. if (pObject) {
  528. O::InitObject(pObject);
  529. }
  530. return (pObject);
  531. };
  532. static void FreeObject(T *pObject)
  533. {
  534. if (pObject) {
  535. O::TermObject(pObject);
  536. }
  537. g_pMalloc->Free(pObject);
  538. }
  539. static void FreeList(T *pObject) {
  540. while (pObject) {
  541. T *pNext;
  542. pNext = *O::Off(pObject);
  543. FreeObject(pObject);
  544. pObject = pNext;
  545. }
  546. };
  547. };
  548. // This template class provides an allocator using an
  549. // instance of the IMalloc interface.
  550. template <class T, DWORD dwNextLinkOffset>
  551. class CAllocObjWithIMalloc :
  552. public CAllocObjBase<T,dwNextLinkOffset>,
  553. public CAllocObjWithIMallocBase<T, CAllocObjBase<T,dwNextLinkOffset> >
  554. {
  555. // nothing
  556. };
  557. #endif