2 * PROJECT: ReactOS win32 kernel mode subsystem
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: win32ss/gdi/ntgdi/gdipool.c
5 * PURPOSE: Static size allocator for user mode object attributes
6 * PROGRAMMERS: Timo Kreuzer
9 /* INCLUDES ******************************************************************/
15 typedef struct _GDI_POOL_SECTION
17 LIST_ENTRY leInUseLink
;
18 LIST_ENTRY leReadyLink
;
27 } GDI_POOL_SECTION
, *PGDI_POOL_SECTION
;
29 typedef struct _GDI_POOL
33 ULONG cjSectionSize
; // 32 * cjAllocSize, rounded up to pages
34 ULONG cSlotsPerSection
;
36 EX_PUSH_LOCK pushlock
; // For pool growth
37 #if DBG_ENABLE_EVENT_LOGGING
41 LIST_ENTRY leInUseList
;
42 LIST_ENTRY leEmptyList
;
43 LIST_ENTRY leReadyList
;
46 #define GDI_POOL_ALLOCATION_GRANULARITY 64 * 1024
50 GdiPoolAllocateSection(PGDI_POOL pPool
)
52 PGDI_POOL_SECTION pSection
;
57 /* Allocate a section object */
58 cjSize
= sizeof(GDI_POOL_SECTION
) + pPool
->cSlotsPerSection
/ sizeof(ULONG
);
59 pSection
= EngAllocMem(0, cjSize
, pPool
->ulTag
);
65 /* Reserve user mode memory */
66 cjSize
= GDI_POOL_ALLOCATION_GRANULARITY
;
68 status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
74 if (!NT_SUCCESS(status
))
80 /* Initialize the section */
81 pSection
->pvBaseAddress
= pvBaseAddress
;
82 pSection
->ulCommitBitmap
= 0;
83 pSection
->cAllocCount
= 0;
84 RtlInitializeBitMap(&pSection
->bitmap
,
86 pPool
->cSlotsPerSection
);
87 RtlClearAllBits(&pSection
->bitmap
);
89 /* Return the section */
95 GdiPoolDeleteSection(PGDI_POOL pPool
, PGDI_POOL_SECTION pSection
)
100 /* Should not have any allocations */
101 if (pSection
->cAllocCount
!= 0)
103 DPRINT1("There are %lu allocations left, section=%p, pool=%p\n",
104 pSection
->cAllocCount
, pSection
, pPool
);
105 DBG_DUMP_EVENT_LIST(&pPool
->slhLog
);
109 /* Release the virtual memory */
110 status
= ZwFreeVirtualMemory(NtCurrentProcess(),
111 &pSection
->pvBaseAddress
,
114 ASSERT(NT_SUCCESS(status
));
116 /* Free the section object */
117 EngFreeMem(pSection
);
125 PGDI_POOL_SECTION pSection
;
126 ULONG ulIndex
, cjOffset
, ulPageBit
;
128 PVOID pvAlloc
, pvBaseAddress
;
132 /* Disable APCs and acquire the pool lock */
133 KeEnterCriticalRegion();
134 ExAcquirePushLockExclusive(&pPool
->pushlock
);
136 /* Check if we have a ready section */
137 if (!IsListEmpty(&pPool
->leReadyList
))
139 /* Get a free section */
140 ple
= pPool
->leReadyList
.Flink
;
141 pSection
= CONTAINING_RECORD(ple
, GDI_POOL_SECTION
, leReadyLink
);
142 if (pSection
->cAllocCount
>= pPool
->cSlotsPerSection
)
144 DPRINT1("pSection->cAllocCount=%lu, pPool->cSlotsPerSection=%lu\n",
145 pSection
->cAllocCount
, pPool
->cSlotsPerSection
);
146 DBG_DUMP_EVENT_LIST(&pPool
->slhLog
);
149 ASSERT(pSection
->cAllocCount
< pPool
->cSlotsPerSection
);
153 /* No, check if we have something on the empty list */
154 if (!IsListEmpty(&pPool
->leEmptyList
))
156 /* Yes, remove it from the empty list */
157 ple
= RemoveHeadList(&pPool
->leEmptyList
);
158 pSection
= CONTAINING_RECORD(ple
, GDI_POOL_SECTION
, leInUseLink
);
159 pPool
->cEmptySections
--;
160 ASSERT(pSection
->cAllocCount
== 0);
164 /* No, allocate a new section */
165 pSection
= GdiPoolAllocateSection(pPool
);
168 DPRINT1("Couldn't allocate a section\n");
174 /* Insert it into the in-use and ready list */
175 InsertHeadList(&pPool
->leInUseList
, &pSection
->leInUseLink
);
176 InsertHeadList(&pPool
->leReadyList
, &pSection
->leReadyLink
);
179 /* Find and set a single bit */
180 ulIndex
= RtlFindClearBitsAndSet(&pSection
->bitmap
, 1, 0);
181 ASSERT(ulIndex
!= MAXULONG
);
183 /* Calculate the allocation address */
184 cjOffset
= ulIndex
* pPool
->cjAllocSize
;
185 pvAlloc
= (PVOID
)((ULONG_PTR
)pSection
->pvBaseAddress
+ cjOffset
);
187 /* Check if memory is comitted */
188 ulPageBit
= 1 << (cjOffset
/ PAGE_SIZE
);
189 ulPageBit
|= 1 << ((cjOffset
+ pPool
->cjAllocSize
- 1) / PAGE_SIZE
);
190 if ((pSection
->ulCommitBitmap
& ulPageBit
) != ulPageBit
)
192 /* Commit the pages */
193 pvBaseAddress
= PAGE_ALIGN(pvAlloc
);
194 cjSize
= ADDRESS_AND_SIZE_TO_SPAN_PAGES(pvAlloc
, pPool
->cjAllocSize
) * PAGE_SIZE
;
195 status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
201 if (!NT_SUCCESS(status
))
207 pSection
->ulCommitBitmap
|= ulPageBit
;
210 /* Increase alloc count */
211 pSection
->cAllocCount
++;
212 ASSERT(RtlNumberOfSetBits(&pSection
->bitmap
) == pSection
->cAllocCount
);
213 DBG_LOGEVENT(&pPool
->slhLog
, EVENT_ALLOCATE
, pvAlloc
);
215 /* Check if section is now busy */
216 if (pSection
->cAllocCount
== pPool
->cSlotsPerSection
)
218 /* Remove the section from the ready list */
219 RemoveEntryList(&pSection
->leReadyLink
);
223 /* Release the pool lock and enable APCs */
224 ExReleasePushLockExclusive(&pPool
->pushlock
);
225 KeLeaveCriticalRegion();
227 DPRINT("GdiPoolallocate: %p\n", pvAlloc
);
238 PGDI_POOL_SECTION pSection
= NULL
;
241 DPRINT("GdiPoolFree: %p\n", pvAlloc
);
243 /* Disable APCs and acquire the pool lock */
244 KeEnterCriticalRegion();
245 ExAcquirePushLockExclusive(&pPool
->pushlock
);
247 /* Loop all used sections */
248 for (ple
= pPool
->leInUseList
.Flink
;
249 ple
!= &pPool
->leInUseList
;
252 /* Get the pointer to the section */
253 pSection
= CONTAINING_RECORD(ple
, GDI_POOL_SECTION
, leInUseLink
);
255 /* Calculate offset */
256 cjOffset
= (ULONG_PTR
)pvAlloc
- (ULONG_PTR
)pSection
->pvBaseAddress
;
258 /* Check if the allocation is from this section */
259 if (cjOffset
< pPool
->cjSectionSize
)
261 /* Calculate the index of the allocation */
262 ulIndex
= cjOffset
/ pPool
->cjAllocSize
;
264 /* Mark it as free */
265 ASSERT(RtlTestBit(&pSection
->bitmap
, ulIndex
) == TRUE
);
266 RtlClearBit(&pSection
->bitmap
, ulIndex
);
268 /* Decrease allocation count */
269 pSection
->cAllocCount
--;
270 ASSERT(RtlNumberOfSetBits(&pSection
->bitmap
) == pSection
->cAllocCount
);
271 DBG_LOGEVENT(&pPool
->slhLog
, EVENT_FREE
, pvAlloc
);
273 /* Check if the section got valid now */
274 if (pSection
->cAllocCount
== pPool
->cSlotsPerSection
- 1)
276 /* Insert it into the ready list */
277 InsertTailList(&pPool
->leReadyList
, &pSection
->leReadyLink
);
279 /* Check if it got empty now */
280 else if (pSection
->cAllocCount
== 0)
282 /* Remove the section from the lists */
283 RemoveEntryList(&pSection
->leInUseLink
);
284 RemoveEntryList(&pSection
->leReadyLink
);
286 if (pPool
->cEmptySections
>= 1)
288 /* Delete the section */
289 GdiPoolDeleteSection(pPool
, pSection
);
293 /* Insert it into the empty list */
294 InsertHeadList(&pPool
->leEmptyList
, &pSection
->leInUseLink
);
295 pPool
->cEmptySections
++;
303 DbgPrint("failed to free. pvAlloc=%p, base=%p, size=%lx\n",
304 pvAlloc
, pSection
? pSection
->pvBaseAddress
: NULL
, pPool
->cjSectionSize
);
309 /* Release the pool lock and enable APCs */
310 ExReleasePushLockExclusive(&pPool
->pushlock
);
311 KeLeaveCriticalRegion();
322 /* Allocate a pool object */
323 pPool
= EngAllocMem(0, sizeof(GDI_POOL
), 'lopG');
324 if (!pPool
) return NULL
;
326 /* Initialize the object */
327 ExInitializePushLock(&pPool
->pushlock
);
328 InitializeListHead(&pPool
->leInUseList
);
329 InitializeListHead(&pPool
->leReadyList
);
330 InitializeListHead(&pPool
->leEmptyList
);
331 pPool
->cEmptySections
= 0;
332 pPool
->cjAllocSize
= cjAllocSize
;
333 pPool
->ulTag
= ulTag
;
334 pPool
->cjSectionSize
= GDI_POOL_ALLOCATION_GRANULARITY
;
335 pPool
->cSlotsPerSection
= pPool
->cjSectionSize
/ cjAllocSize
;
336 DBG_INITLOG(&pPool
->slhLog
);
343 GdiPoolDestroy(PGDI_POOL pPool
)
345 PGDI_POOL_SECTION pSection
;
348 /* Loop all empty sections, removing them */
349 while (!IsListEmpty(&pPool
->leEmptyList
))
351 /* Delete the section */
352 ple
= RemoveHeadList(&pPool
->leEmptyList
);
353 pSection
= CONTAINING_RECORD(ple
, GDI_POOL_SECTION
, leInUseLink
);
354 GdiPoolDeleteSection(pPool
, pSection
);
357 /* Loop all ready sections, removing them */
358 while (!IsListEmpty(&pPool
->leInUseList
))
360 /* Delete the section */
361 ple
= RemoveHeadList(&pPool
->leInUseList
);
362 pSection
= CONTAINING_RECORD(ple
, GDI_POOL_SECTION
, leInUseLink
);
363 GdiPoolDeleteSection(pPool
, pSection
);
366 DBG_CLEANUP_EVENT_LIST(&pPool
->slhLog
);