2 * PROJECT: ReactOS win32 kernel mode subsystem
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: subsystems/win32/win32k/objects/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
;
131 /* Disable APCs and acquire the pool lock */
132 KeEnterCriticalRegion();
133 ExAcquirePushLockExclusive(&pPool
->pushlock
);
135 /* Check if we have a ready section */
136 if (!IsListEmpty(&pPool
->leReadyList
))
138 /* Get a free section */
139 ple
= pPool
->leReadyList
.Flink
;
140 pSection
= CONTAINING_RECORD(ple
, GDI_POOL_SECTION
, leReadyLink
);
141 if (pSection
->cAllocCount
>= pPool
->cSlotsPerSection
)
143 DPRINT1("pSection->cAllocCount=%lu, pPool->cSlotsPerSection=%lu\n",
144 pSection
->cAllocCount
, pPool
->cSlotsPerSection
);
145 DBG_DUMP_EVENT_LIST(&pPool
->slhLog
);
148 ASSERT(pSection
->cAllocCount
< pPool
->cSlotsPerSection
);
152 /* No, check if we have something on the empty list */
153 if (!IsListEmpty(&pPool
->leEmptyList
))
155 /* Yes, remove it from the empty list */
156 ple
= RemoveHeadList(&pPool
->leEmptyList
);
157 pSection
= CONTAINING_RECORD(ple
, GDI_POOL_SECTION
, leInUseLink
);
158 pPool
->cEmptySections
--;
159 ASSERT(pSection
->cAllocCount
== 0);
163 /* No, allocate a new section */
164 pSection
= GdiPoolAllocateSection(pPool
);
167 DPRINT1("Couldn't allocate a section\n");
173 /* Insert it into the in-use and ready list */
174 InsertHeadList(&pPool
->leInUseList
, &pSection
->leInUseLink
);
175 InsertHeadList(&pPool
->leReadyList
, &pSection
->leReadyLink
);
178 /* Find and set a single bit */
179 ulIndex
= RtlFindClearBitsAndSet(&pSection
->bitmap
, 1, 0);
180 ASSERT(ulIndex
!= MAXULONG
);
182 /* Calculate the allocation address */
183 cjOffset
= ulIndex
* pPool
->cjAllocSize
;
184 pvAlloc
= (PVOID
)((ULONG_PTR
)pSection
->pvBaseAddress
+ cjOffset
);
186 /* Check if memory is comitted */
187 ulPageBit
= 1 << (cjOffset
/ PAGE_SIZE
);
188 ulPageBit
|= 1 << ((cjOffset
+ pPool
->cjAllocSize
- 1) / PAGE_SIZE
);
189 if ((pSection
->ulCommitBitmap
& ulPageBit
) != ulPageBit
)
191 /* Commit the pages */
192 pvBaseAddress
= PAGE_ALIGN(pvAlloc
);
193 cjSize
= ADDRESS_AND_SIZE_TO_SPAN_PAGES(pvAlloc
, pPool
->cjAllocSize
) * PAGE_SIZE
;
194 ZwAllocateVirtualMemory(NtCurrentProcess(),
201 pSection
->ulCommitBitmap
|= ulPageBit
;
204 /* Increase alloc count */
205 pSection
->cAllocCount
++;
206 ASSERT(RtlNumberOfSetBits(&pSection
->bitmap
) == pSection
->cAllocCount
);
207 DBG_LOGEVENT(&pPool
->slhLog
, EVENT_ALLOCATE
, pvAlloc
);
209 /* Check if section is now busy */
210 if (pSection
->cAllocCount
== pPool
->cSlotsPerSection
)
212 /* Remove the section from the ready list */
213 RemoveEntryList(&pSection
->leReadyLink
);
217 /* Release the pool lock and enable APCs */
218 ExReleasePushLockExclusive(&pPool
->pushlock
);
219 KeLeaveCriticalRegion();
221 DPRINT("GdiPoolallocate: %p\n", pvAlloc
);
232 PGDI_POOL_SECTION pSection
= NULL
;
235 DPRINT("GdiPoolFree: %p\n", pvAlloc
);
237 /* Disable APCs and acquire the pool lock */
238 KeEnterCriticalRegion();
239 ExAcquirePushLockExclusive(&pPool
->pushlock
);
241 /* Loop all used sections */
242 for (ple
= pPool
->leInUseList
.Flink
;
243 ple
!= &pPool
->leInUseList
;
246 /* Get the pointer to the section */
247 pSection
= CONTAINING_RECORD(ple
, GDI_POOL_SECTION
, leInUseLink
);
249 /* Calculate offset */
250 cjOffset
= (ULONG_PTR
)pvAlloc
- (ULONG_PTR
)pSection
->pvBaseAddress
;
252 /* Check if the allocation is from this section */
253 if (cjOffset
< pPool
->cjSectionSize
)
255 /* Calculate the index of the allocation */
256 ulIndex
= cjOffset
/ pPool
->cjAllocSize
;
258 /* Mark it as free */
259 ASSERT(RtlTestBit(&pSection
->bitmap
, ulIndex
) == TRUE
);
260 RtlClearBit(&pSection
->bitmap
, ulIndex
);
262 /* Decrease allocation count */
263 pSection
->cAllocCount
--;
264 ASSERT(RtlNumberOfSetBits(&pSection
->bitmap
) == pSection
->cAllocCount
);
265 DBG_LOGEVENT(&pPool
->slhLog
, EVENT_FREE
, pvAlloc
);
267 /* Check if the section got valid now */
268 if (pSection
->cAllocCount
== pPool
->cSlotsPerSection
- 1)
270 /* Insert it into the ready list */
271 InsertTailList(&pPool
->leReadyList
, &pSection
->leReadyLink
);
273 /* Check if it got empty now */
274 else if (pSection
->cAllocCount
== 0)
276 /* Remove the section from the lists */
277 RemoveEntryList(&pSection
->leInUseLink
);
278 RemoveEntryList(&pSection
->leReadyLink
);
280 if (pPool
->cEmptySections
>= 1)
282 /* Delete the section */
283 GdiPoolDeleteSection(pPool
, pSection
);
287 /* Insert it into the empty list */
288 InsertHeadList(&pPool
->leEmptyList
, &pSection
->leInUseLink
);
289 pPool
->cEmptySections
++;
297 DbgPrint("failed to free. pvAlloc=%p, base=%p, size=%lx\n",
298 pvAlloc
, pSection
? pSection
->pvBaseAddress
: NULL
, pPool
->cjSectionSize
);
303 /* Release the pool lock and enable APCs */
304 ExReleasePushLockExclusive(&pPool
->pushlock
);
305 KeLeaveCriticalRegion();
316 /* Allocate a pool object */
317 pPool
= EngAllocMem(0, sizeof(GDI_POOL
), 'lopG');
318 if (!pPool
) return NULL
;
320 /* Initialize the object */
321 ExInitializePushLock(&pPool
->pushlock
);
322 InitializeListHead(&pPool
->leInUseList
);
323 InitializeListHead(&pPool
->leReadyList
);
324 InitializeListHead(&pPool
->leEmptyList
);
325 pPool
->cEmptySections
= 0;
326 pPool
->cjAllocSize
= cjAllocSize
;
327 pPool
->ulTag
= ulTag
;
328 pPool
->cjSectionSize
= GDI_POOL_ALLOCATION_GRANULARITY
;
329 pPool
->cSlotsPerSection
= pPool
->cjSectionSize
/ cjAllocSize
;
330 DBG_INITLOG(&pPool
->slhLog
);
337 GdiPoolDestroy(PGDI_POOL pPool
)
339 PGDI_POOL_SECTION pSection
;
342 /* Loop all empty sections, removing them */
343 while (!IsListEmpty(&pPool
->leEmptyList
))
345 /* Delete the section */
346 ple
= RemoveHeadList(&pPool
->leEmptyList
);
347 pSection
= CONTAINING_RECORD(ple
, GDI_POOL_SECTION
, leInUseLink
);
348 GdiPoolDeleteSection(pPool
, pSection
);
351 /* Loop all ready sections, removing them */
352 while (!IsListEmpty(&pPool
->leInUseList
))
354 /* Delete the section */
355 ple
= RemoveHeadList(&pPool
->leInUseList
);
356 pSection
= CONTAINING_RECORD(ple
, GDI_POOL_SECTION
, leInUseLink
);
357 GdiPoolDeleteSection(pPool
, pSection
);
360 DBG_CLEANUP_EVENT_LIST(&pPool
->slhLog
);