Synchronize with trunk's revision r57652.
[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 }
159 else
160 {
161 /* No, allocate a new section */
162 pSection = GdiPoolAllocateSection(pPool);
163 if (!pSection)
164 {
165 DPRINT1("Couldn't allocate a section\n");
166 pvAlloc = NULL;
167 goto done;
168 }
169
170 /* Insert it into the ready list */
171 InsertHeadList(&pPool->leReadyList, &pSection->leReadyLink);
172 }
173
174 /* Insert it into the in-use list */
175 InsertHeadList(&pPool->leInUseList, &pSection->leInUseLink);
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 DBG_LOGEVENT(&pPool->slhLog, EVENT_ALLOCATE, pvAlloc);
207
208 /* Check if section is now busy */
209 if (pSection->cAllocCount == pPool->cSlotsPerSection)
210 {
211 /* Remove the section from the ready list */
212 RemoveEntryList(&pSection->leReadyLink);
213 }
214
215 done:
216 /* Release the pool lock and enable APCs */
217 ExReleasePushLockExclusive(&pPool->pushlock);
218 KeLeaveCriticalRegion();
219
220 DPRINT("GdiPoolallocate: %p\n", pvAlloc);
221 return pvAlloc;
222 }
223
224 VOID
225 NTAPI
226 GdiPoolFree(
227 PGDI_POOL pPool,
228 PVOID pvAlloc)
229 {
230 PLIST_ENTRY ple;
231 PGDI_POOL_SECTION pSection = NULL;
232 ULONG_PTR cjOffset;
233 ULONG ulIndex;
234 DPRINT("GdiPoolFree: %p\n", pvAlloc);
235
236 /* Disable APCs and acquire the pool lock */
237 KeEnterCriticalRegion();
238 ExAcquirePushLockExclusive(&pPool->pushlock);
239
240 /* Loop all used sections */
241 for (ple = pPool->leInUseList.Flink;
242 ple != &pPool->leInUseList;
243 ple = ple->Flink)
244 {
245 /* Get the pointer to the section */
246 pSection = CONTAINING_RECORD(ple, GDI_POOL_SECTION, leInUseLink);
247
248 /* Calculate offset */
249 cjOffset = (ULONG_PTR)pvAlloc - (ULONG_PTR)pSection->pvBaseAddress;
250
251 /* Check if the allocation is from this section */
252 if (cjOffset < pPool->cjSectionSize)
253 {
254 /* Calculate the index of the allocation */
255 ulIndex = cjOffset / pPool->cjAllocSize;
256
257 /* Mark it as free */
258 ASSERT(RtlTestBit(&pSection->bitmap, ulIndex) == TRUE);
259 RtlClearBit(&pSection->bitmap, ulIndex);
260
261 /* Decrease allocation count */
262 pSection->cAllocCount--;
263 DBG_LOGEVENT(&pPool->slhLog, EVENT_FREE, pvAlloc);
264
265 /* Check if the section got valid now */
266 if (pSection->cAllocCount == pPool->cSlotsPerSection - 1)
267 {
268 /* Insert it into the ready list */
269 InsertTailList(&pPool->leReadyList, &pSection->leReadyLink);
270 }
271 /* Check if it got empty now */
272 else if (pSection->cAllocCount == 0)
273 {
274 /* Remove the section from the lists */
275 RemoveEntryList(&pSection->leInUseLink);
276 RemoveEntryList(&pSection->leReadyLink);
277
278 if (pPool->cEmptySections > 1)
279 {
280 /* Delete the section */
281 GdiPoolDeleteSection(pPool, pSection);
282 }
283 else
284 {
285 /* Insert it into the empty list */
286 InsertHeadList(&pPool->leEmptyList, &pSection->leInUseLink);
287 pPool->cEmptySections++;
288 }
289 }
290
291 goto done;
292 }
293 }
294
295 DbgPrint("failed to free. pvAlloc=%p, base=%p, size=%lx\n",
296 pvAlloc, pSection ? pSection->pvBaseAddress : NULL, pPool->cjSectionSize);
297 ASSERT(FALSE);
298 // KeBugCheck()
299
300 done:
301 /* Release the pool lock and enable APCs */
302 ExReleasePushLockExclusive(&pPool->pushlock);
303 KeLeaveCriticalRegion();
304 }
305
306 PGDI_POOL
307 NTAPI
308 GdiPoolCreate(
309 ULONG cjAllocSize,
310 ULONG ulTag)
311 {
312 PGDI_POOL pPool;
313
314 /* Allocate a pool object */
315 pPool = EngAllocMem(0, sizeof(GDI_POOL), 'lopG');
316 if (!pPool) return NULL;
317
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);
329
330 return pPool;
331 }
332
333 VOID
334 NTAPI
335 GdiPoolDestroy(PGDI_POOL pPool)
336 {
337 PGDI_POOL_SECTION pSection;
338 PLIST_ENTRY ple;
339
340 /* Loop all empty sections, removing them */
341 while (!IsListEmpty(&pPool->leEmptyList))
342 {
343 /* Delete the section */
344 ple = RemoveHeadList(&pPool->leEmptyList);
345 pSection = CONTAINING_RECORD(ple, GDI_POOL_SECTION, leInUseLink);
346 GdiPoolDeleteSection(pPool, pSection);
347 }
348
349 /* Loop all ready sections, removing them */
350 while (!IsListEmpty(&pPool->leInUseList))
351 {
352 /* Delete the section */
353 ple = RemoveHeadList(&pPool->leInUseList);
354 pSection = CONTAINING_RECORD(ple, GDI_POOL_SECTION, leInUseLink);
355 GdiPoolDeleteSection(pPool, pSection);
356 }
357
358 DBG_CLEANUP_EVENT_LIST(&pPool->slhLog);
359
360 EngFreeMem(pPool);
361 }