Sync with trunk revision r58045 to bring the corrections on configure.cmd and on...
[reactos.git] / win32ss / gdi / ntgdi / gdipool.c
1 /*
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
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include <win32k.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 typedef struct _GDI_POOL_SECTION
16 {
17 LIST_ENTRY leInUseLink;
18 LIST_ENTRY leReadyLink;
19
20 PVOID pvBaseAddress;
21
22 ULONG ulCommitBitmap;
23 ULONG cAllocCount;
24
25 RTL_BITMAP bitmap;
26 ULONG aulBits[1];
27 } GDI_POOL_SECTION, *PGDI_POOL_SECTION;
28
29 typedef struct _GDI_POOL
30 {
31 ULONG ulTag;
32 ULONG cjAllocSize;
33 ULONG cjSectionSize; // 32 * cjAllocSize, rounded up to pages
34 ULONG cSlotsPerSection;
35 ULONG cEmptySections;
36 EX_PUSH_LOCK pushlock; // For pool growth
37 #if DBG_ENABLE_EVENT_LOGGING
38 SLIST_HEADER slhLog;
39 #endif
40
41 LIST_ENTRY leInUseList;
42 LIST_ENTRY leEmptyList;
43 LIST_ENTRY leReadyList;
44 } GDI_POOL;
45
46 #define GDI_POOL_ALLOCATION_GRANULARITY 64 * 1024
47
48 static
49 PGDI_POOL_SECTION
50 GdiPoolAllocateSection(PGDI_POOL pPool)
51 {
52 PGDI_POOL_SECTION pSection;
53 PVOID pvBaseAddress;
54 SIZE_T cjSize;
55 NTSTATUS status;
56
57 /* Allocate a section object */
58 cjSize = sizeof(GDI_POOL_SECTION) + pPool->cSlotsPerSection / sizeof(ULONG);
59 pSection = EngAllocMem(0, cjSize, pPool->ulTag);
60 if (!pSection)
61 {
62 return NULL;
63 }
64
65 /* Reserve user mode memory */
66 cjSize = GDI_POOL_ALLOCATION_GRANULARITY;
67 pvBaseAddress = NULL;
68 status = ZwAllocateVirtualMemory(NtCurrentProcess(),
69 &pvBaseAddress,
70 0,
71 &cjSize,
72 MEM_RESERVE,
73 PAGE_READWRITE);
74 if (!NT_SUCCESS(status))
75 {
76 EngFreeMem(pSection);
77 return NULL;
78 }
79
80 /* Initialize the section */
81 pSection->pvBaseAddress = pvBaseAddress;
82 pSection->ulCommitBitmap = 0;
83 pSection->cAllocCount = 0;
84 RtlInitializeBitMap(&pSection->bitmap,
85 pSection->aulBits,
86 pPool->cSlotsPerSection);
87 RtlClearAllBits(&pSection->bitmap);
88
89 /* Return the section */
90 return pSection;
91 }
92
93 static
94 VOID
95 GdiPoolDeleteSection(PGDI_POOL pPool, PGDI_POOL_SECTION pSection)
96 {
97 NTSTATUS status;
98 SIZE_T cjSize = 0;
99
100 /* Should not have any allocations */
101 if (pSection->cAllocCount != 0)
102 {
103 DPRINT1("There are %lu allocations left, section=%p, pool=%p\n",
104 pSection->cAllocCount, pSection, pPool);
105 DBG_DUMP_EVENT_LIST(&pPool->slhLog);
106 ASSERT(FALSE);
107 }
108
109 /* Release the virtual memory */
110 status = ZwFreeVirtualMemory(NtCurrentProcess(),
111 &pSection->pvBaseAddress,
112 &cjSize,
113 MEM_RELEASE);
114 ASSERT(NT_SUCCESS(status));
115
116 /* Free the section object */
117 EngFreeMem(pSection);
118 }
119
120 PVOID
121 NTAPI
122 GdiPoolAllocate(
123 PGDI_POOL pPool)
124 {
125 PGDI_POOL_SECTION pSection;
126 ULONG ulIndex, cjOffset, ulPageBit;
127 PLIST_ENTRY ple;
128 PVOID pvAlloc, pvBaseAddress;
129 SIZE_T cjSize;
130
131 /* Disable APCs and acquire the pool lock */
132 KeEnterCriticalRegion();
133 ExAcquirePushLockExclusive(&pPool->pushlock);
134
135 /* Check if we have a ready section */
136 if (!IsListEmpty(&pPool->leReadyList))
137 {
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)
142 {
143 DPRINT1("pSection->cAllocCount=%lu, pPool->cSlotsPerSection=%lu\n",
144 pSection->cAllocCount, pPool->cSlotsPerSection);
145 DBG_DUMP_EVENT_LIST(&pPool->slhLog);
146 ASSERT(FALSE);
147 }
148 ASSERT(pSection->cAllocCount < pPool->cSlotsPerSection);
149 }
150 else
151 {
152 /* No, check if we have something on the empty list */
153 if (!IsListEmpty(&pPool->leEmptyList))
154 {
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);
160 }
161 else
162 {
163 /* No, allocate a new section */
164 pSection = GdiPoolAllocateSection(pPool);
165 if (!pSection)
166 {
167 DPRINT1("Couldn't allocate a section\n");
168 pvAlloc = NULL;
169 goto done;
170 }
171 }
172
173 /* Insert it into the in-use and ready list */
174 InsertHeadList(&pPool->leInUseList, &pSection->leInUseLink);
175 InsertHeadList(&pPool->leReadyList, &pSection->leReadyLink);
176 }
177
178 /* Find and set a single bit */
179 ulIndex = RtlFindClearBitsAndSet(&pSection->bitmap, 1, 0);
180 ASSERT(ulIndex != MAXULONG);
181
182 /* Calculate the allocation address */
183 cjOffset = ulIndex * pPool->cjAllocSize;
184 pvAlloc = (PVOID)((ULONG_PTR)pSection->pvBaseAddress + cjOffset);
185
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)
190 {
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(),
195 &pvBaseAddress,
196 0,
197 &cjSize,
198 MEM_COMMIT,
199 PAGE_READWRITE);
200
201 pSection->ulCommitBitmap |= ulPageBit;
202 }
203
204 /* Increase alloc count */
205 pSection->cAllocCount++;
206 ASSERT(RtlNumberOfSetBits(&pSection->bitmap) == pSection->cAllocCount);
207 DBG_LOGEVENT(&pPool->slhLog, EVENT_ALLOCATE, pvAlloc);
208
209 /* Check if section is now busy */
210 if (pSection->cAllocCount == pPool->cSlotsPerSection)
211 {
212 /* Remove the section from the ready list */
213 RemoveEntryList(&pSection->leReadyLink);
214 }
215
216 done:
217 /* Release the pool lock and enable APCs */
218 ExReleasePushLockExclusive(&pPool->pushlock);
219 KeLeaveCriticalRegion();
220
221 DPRINT("GdiPoolallocate: %p\n", pvAlloc);
222 return pvAlloc;
223 }
224
225 VOID
226 NTAPI
227 GdiPoolFree(
228 PGDI_POOL pPool,
229 PVOID pvAlloc)
230 {
231 PLIST_ENTRY ple;
232 PGDI_POOL_SECTION pSection = NULL;
233 ULONG_PTR cjOffset;
234 ULONG ulIndex;
235 DPRINT("GdiPoolFree: %p\n", pvAlloc);
236
237 /* Disable APCs and acquire the pool lock */
238 KeEnterCriticalRegion();
239 ExAcquirePushLockExclusive(&pPool->pushlock);
240
241 /* Loop all used sections */
242 for (ple = pPool->leInUseList.Flink;
243 ple != &pPool->leInUseList;
244 ple = ple->Flink)
245 {
246 /* Get the pointer to the section */
247 pSection = CONTAINING_RECORD(ple, GDI_POOL_SECTION, leInUseLink);
248
249 /* Calculate offset */
250 cjOffset = (ULONG_PTR)pvAlloc - (ULONG_PTR)pSection->pvBaseAddress;
251
252 /* Check if the allocation is from this section */
253 if (cjOffset < pPool->cjSectionSize)
254 {
255 /* Calculate the index of the allocation */
256 ulIndex = cjOffset / pPool->cjAllocSize;
257
258 /* Mark it as free */
259 ASSERT(RtlTestBit(&pSection->bitmap, ulIndex) == TRUE);
260 RtlClearBit(&pSection->bitmap, ulIndex);
261
262 /* Decrease allocation count */
263 pSection->cAllocCount--;
264 ASSERT(RtlNumberOfSetBits(&pSection->bitmap) == pSection->cAllocCount);
265 DBG_LOGEVENT(&pPool->slhLog, EVENT_FREE, pvAlloc);
266
267 /* Check if the section got valid now */
268 if (pSection->cAllocCount == pPool->cSlotsPerSection - 1)
269 {
270 /* Insert it into the ready list */
271 InsertTailList(&pPool->leReadyList, &pSection->leReadyLink);
272 }
273 /* Check if it got empty now */
274 else if (pSection->cAllocCount == 0)
275 {
276 /* Remove the section from the lists */
277 RemoveEntryList(&pSection->leInUseLink);
278 RemoveEntryList(&pSection->leReadyLink);
279
280 if (pPool->cEmptySections >= 1)
281 {
282 /* Delete the section */
283 GdiPoolDeleteSection(pPool, pSection);
284 }
285 else
286 {
287 /* Insert it into the empty list */
288 InsertHeadList(&pPool->leEmptyList, &pSection->leInUseLink);
289 pPool->cEmptySections++;
290 }
291 }
292
293 goto done;
294 }
295 }
296
297 DbgPrint("failed to free. pvAlloc=%p, base=%p, size=%lx\n",
298 pvAlloc, pSection ? pSection->pvBaseAddress : NULL, pPool->cjSectionSize);
299 ASSERT(FALSE);
300 // KeBugCheck()
301
302 done:
303 /* Release the pool lock and enable APCs */
304 ExReleasePushLockExclusive(&pPool->pushlock);
305 KeLeaveCriticalRegion();
306 }
307
308 PGDI_POOL
309 NTAPI
310 GdiPoolCreate(
311 ULONG cjAllocSize,
312 ULONG ulTag)
313 {
314 PGDI_POOL pPool;
315
316 /* Allocate a pool object */
317 pPool = EngAllocMem(0, sizeof(GDI_POOL), 'lopG');
318 if (!pPool) return NULL;
319
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);
331
332 return pPool;
333 }
334
335 VOID
336 NTAPI
337 GdiPoolDestroy(PGDI_POOL pPool)
338 {
339 PGDI_POOL_SECTION pSection;
340 PLIST_ENTRY ple;
341
342 /* Loop all empty sections, removing them */
343 while (!IsListEmpty(&pPool->leEmptyList))
344 {
345 /* Delete the section */
346 ple = RemoveHeadList(&pPool->leEmptyList);
347 pSection = CONTAINING_RECORD(ple, GDI_POOL_SECTION, leInUseLink);
348 GdiPoolDeleteSection(pPool, pSection);
349 }
350
351 /* Loop all ready sections, removing them */
352 while (!IsListEmpty(&pPool->leInUseList))
353 {
354 /* Delete the section */
355 ple = RemoveHeadList(&pPool->leInUseList);
356 pSection = CONTAINING_RECORD(ple, GDI_POOL_SECTION, leInUseLink);
357 GdiPoolDeleteSection(pPool, pSection);
358 }
359
360 DBG_CLEANUP_EVENT_LIST(&pPool->slhLog);
361
362 EngFreeMem(pPool);
363 }