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
);
161 /* No, allocate a new section */
162 pSection
= GdiPoolAllocateSection(pPool
);
165 DPRINT1("Couldn't allocate a section\n");
170 /* Insert it into the ready list */
171 InsertHeadList(&pPool
->leReadyList
, &pSection
->leReadyLink
);
174 /* Insert it into the in-use list */
175 InsertHeadList(&pPool
->leInUseList
, &pSection
->leInUseLink
);
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 DBG_LOGEVENT(&pPool
->slhLog
, EVENT_ALLOCATE
, pvAlloc
);
208 /* Check if section is now busy */
209 if (pSection
->cAllocCount
== pPool
->cSlotsPerSection
)
211 /* Remove the section from the ready list */
212 RemoveEntryList(&pSection
->leReadyLink
);
216 /* Release the pool lock and enable APCs */
217 ExReleasePushLockExclusive(&pPool
->pushlock
);
218 KeLeaveCriticalRegion();
220 DPRINT("GdiPoolallocate: %p\n", pvAlloc
);
231 PGDI_POOL_SECTION pSection
= NULL
;
234 DPRINT("GdiPoolFree: %p\n", pvAlloc
);
236 /* Disable APCs and acquire the pool lock */
237 KeEnterCriticalRegion();
238 ExAcquirePushLockExclusive(&pPool
->pushlock
);
240 /* Loop all used sections */
241 for (ple
= pPool
->leInUseList
.Flink
;
242 ple
!= &pPool
->leInUseList
;
245 /* Get the pointer to the section */
246 pSection
= CONTAINING_RECORD(ple
, GDI_POOL_SECTION
, leInUseLink
);
248 /* Calculate offset */
249 cjOffset
= (ULONG_PTR
)pvAlloc
- (ULONG_PTR
)pSection
->pvBaseAddress
;
251 /* Check if the allocation is from this section */
252 if (cjOffset
< pPool
->cjSectionSize
)
254 /* Calculate the index of the allocation */
255 ulIndex
= cjOffset
/ pPool
->cjAllocSize
;
257 /* Mark it as free */
258 ASSERT(RtlTestBit(&pSection
->bitmap
, ulIndex
) == TRUE
);
259 RtlClearBit(&pSection
->bitmap
, ulIndex
);
261 /* Decrease allocation count */
262 pSection
->cAllocCount
--;
263 DBG_LOGEVENT(&pPool
->slhLog
, EVENT_FREE
, pvAlloc
);
265 /* Check if the section got valid now */
266 if (pSection
->cAllocCount
== pPool
->cSlotsPerSection
- 1)
268 /* Insert it into the ready list */
269 InsertTailList(&pPool
->leReadyList
, &pSection
->leReadyLink
);
271 /* Check if it got empty now */
272 else if (pSection
->cAllocCount
== 0)
274 /* Remove the section from the lists */
275 RemoveEntryList(&pSection
->leInUseLink
);
276 RemoveEntryList(&pSection
->leReadyLink
);
278 if (pPool
->cEmptySections
> 1)
280 /* Delete the section */
281 GdiPoolDeleteSection(pPool
, pSection
);
285 /* Insert it into the empty list */
286 InsertHeadList(&pPool
->leEmptyList
, &pSection
->leInUseLink
);
287 pPool
->cEmptySections
++;
295 DbgPrint("failed to free. pvAlloc=%p, base=%p, size=%lx\n",
296 pvAlloc
, pSection
? pSection
->pvBaseAddress
: NULL
, pPool
->cjSectionSize
);
301 /* Release the pool lock and enable APCs */
302 ExReleasePushLockExclusive(&pPool
->pushlock
);
303 KeLeaveCriticalRegion();
314 /* Allocate a pool object */
315 pPool
= EngAllocMem(0, sizeof(GDI_POOL
), 'lopG');
316 if (!pPool
) return NULL
;
318 /* Initialize the object */
319 ExInitializePushLock(&pPool
->pushlock
);
320 InitializeListHead(&pPool
->leInUseList
);
321 InitializeListHead(&pPool
->leReadyList
);
322 InitializeListHead(&pPool
->leEmptyList
);
323 pPool
->cEmptySections
= 0;
324 pPool
->cjAllocSize
= cjAllocSize
;
325 pPool
->ulTag
= ulTag
;
326 pPool
->cjSectionSize
= GDI_POOL_ALLOCATION_GRANULARITY
;
327 pPool
->cSlotsPerSection
= pPool
->cjSectionSize
/ cjAllocSize
;
328 DBG_INITLOG(&pPool
->slhLog
);
335 GdiPoolDestroy(PGDI_POOL pPool
)
337 PGDI_POOL_SECTION pSection
;
340 /* Loop all empty sections, removing them */
341 while (!IsListEmpty(&pPool
->leEmptyList
))
343 /* Delete the section */
344 ple
= RemoveHeadList(&pPool
->leEmptyList
);
345 pSection
= CONTAINING_RECORD(ple
, GDI_POOL_SECTION
, leInUseLink
);
346 GdiPoolDeleteSection(pPool
, pSection
);
349 /* Loop all ready sections, removing them */
350 while (!IsListEmpty(&pPool
->leInUseList
))
352 /* Delete the section */
353 ple
= RemoveHeadList(&pPool
->leInUseList
);
354 pSection
= CONTAINING_RECORD(ple
, GDI_POOL_SECTION
, leInUseLink
);
355 GdiPoolDeleteSection(pPool
, pSection
);
358 DBG_CLEANUP_EVENT_LIST(&pPool
->slhLog
);