Author: tkreuzer
Date: Fri Apr 15 20:14:44 2011
New Revision: 51357

URL: http://svn.reactos.org/svn/reactos?rev=51357&view=rev
Log:
[WIN32K]
Implement gdi pool. An allocator for user mode gdi object attributes.
The old method allocated a 4k page for every object, wasting 4k physical memory 
and 64k address space (allcoation granularity)
The new allocator creates a per process pool for each object attribute type. 
Allocations are done from "sections" that start with 1 page and grow 
dynamically up to 64k, if neccessary a new section is allocated. This will use 
about 1/10 of memory for dc attributes and 1/512 for brush attributes. Also 
allocation is way faster. Caching object attributes is not neccessary anymore.

Added:
    trunk/reactos/subsystems/win32/win32k/objects/gdipool.c   (with props)

Added: trunk/reactos/subsystems/win32/win32k/objects/gdipool.c
URL: 
http://svn.reactos.org/svn/reactos/trunk/reactos/subsystems/win32/win32k/objects/gdipool.c?rev=51357&view=auto
==============================================================================
--- trunk/reactos/subsystems/win32/win32k/objects/gdipool.c (added)
+++ trunk/reactos/subsystems/win32/win32k/objects/gdipool.c [iso-8859-1] Fri 
Apr 15 20:14:44 2011
@@ -1,0 +1,336 @@
+/*
+ * PROJECT:         ReactOS win32 kernel mode subsystem
+ * LICENSE:         GPL - See COPYING in the top level directory
+ * FILE:            subsystems/win32/win32k/objects/gdiobj.c
+ * PURPOSE:         Static size allocator for user mode object attributes
+ * PROGRAMMERS:     Timo Kreuzer
+ */
+
+/* INCLUDES ******************************************************************/
+
+#include <win32k.h>
+#define NDEBUG
+#include <debug.h>
+
+typedef struct _GDI_POOL_SECTION
+{
+    LIST_ENTRY leInUseLink;
+    LIST_ENTRY leReadyLink;
+
+    PVOID pvBaseAddress;
+
+    ULONG ulCommitBitmap;
+    ULONG cAllocCount;
+
+    RTL_BITMAP bitmap;
+    ULONG aulBits[1];
+} GDI_POOL_SECTION, *PGDI_POOL_SECTION;
+
+typedef struct _GDI_POOL
+{
+    ULONG ulTag;
+    ULONG cjAllocSize;
+    ULONG cjSectionSize; // 32 * cjAllocSize, rounded up to pages
+    ULONG cSlotsPerSection;
+    ULONG cEmptySections;
+    EX_PUSH_LOCK pushlock; // for pool growth
+
+    LIST_ENTRY leInUseList;
+    LIST_ENTRY leEmptyList;
+    LIST_ENTRY leReadyList;
+} GDI_POOL;
+
+#define GDI_POOL_ALLOCATION_GRANULARITY 64 * 1024
+
+static
+PGDI_POOL_SECTION
+GdiPoolAllocateSection(PGDI_POOL pPool)
+{
+    PGDI_POOL_SECTION pSection;
+    PVOID pvBaseAddress;
+    SIZE_T cjSize;
+    NTSTATUS status;
+
+    /* Allocate a section object */
+    cjSize = sizeof(GDI_POOL_SECTION) + pPool->cSlotsPerSection / 
sizeof(ULONG);
+    pSection = EngAllocMem(0, cjSize, pPool->ulTag);
+    if (!pSection)
+    {
+        return NULL;
+    }
+
+    /* Reserve user mode memory */
+    cjSize = GDI_POOL_ALLOCATION_GRANULARITY;
+    pvBaseAddress = NULL;
+    status = ZwAllocateVirtualMemory(NtCurrentProcess(),
+                                     &pvBaseAddress,
+                                     0,
+                                     &cjSize,
+                                     MEM_RESERVE,
+                                     PAGE_READWRITE);
+    if (!NT_SUCCESS(status))
+    {
+        EngFreeMem(pSection);
+        return NULL;
+    }
+
+    /* Initialize the section */
+    pSection->pvBaseAddress = pvBaseAddress;
+    pSection->ulCommitBitmap = 0;
+    pSection->cAllocCount = 0;
+    RtlInitializeBitMap(&pSection->bitmap,
+                        pSection->aulBits,
+                        pPool->cSlotsPerSection);
+    RtlClearAllBits(&pSection->bitmap);
+
+    /* Return the section */
+    return pSection;
+}
+
+static
+VOID
+GdiPoolDeleteSection(PGDI_POOL pPool, PGDI_POOL_SECTION pSection)
+{
+    NTSTATUS status;
+    SIZE_T cjSize = 0;
+
+    /* Should not have any allocations */
+    ASSERT(pSection->cAllocCount == 0);
+
+    /* Release the virtual memory */
+    status = ZwFreeVirtualMemory(NtCurrentProcess(),
+                                 &pSection->pvBaseAddress,
+                                 &cjSize,
+                                 MEM_RELEASE);
+    ASSERT(NT_SUCCESS(status));
+
+    /* Free the section object */
+    EngFreeMem(pSection);
+}
+
+PVOID
+NTAPI
+GdiPoolAllocate(
+    PGDI_POOL pPool)
+{
+    PGDI_POOL_SECTION pSection;
+    ULONG ulIndex, cjOffset, ulPageBit;
+    PLIST_ENTRY ple;
+    PVOID pvAlloc, pvBaseAddress;
+    SIZE_T cjSize;
+    NTSTATUS status;
+
+    /* Disable APCs and acquire the pool lock */
+    KeEnterCriticalRegion();
+    ExAcquirePushLockExclusive(&pPool->pushlock);
+
+    /* Check if we have a ready section */
+    if (!IsListEmpty(&pPool->leReadyList))
+    {
+        /* Get a free section */
+        ple = pPool->leReadyList.Flink;
+        pSection = CONTAINING_RECORD(ple, GDI_POOL_SECTION, leReadyLink);
+        ASSERT(pSection->cAllocCount < pPool->cSlotsPerSection);
+    }
+    else
+    {
+        /* No, check if we have something on the empty list */
+        if (!IsListEmpty(&pPool->leEmptyList))
+        {
+            /* Yes, remove it from the empty list */
+            ple = RemoveHeadList(&pPool->leEmptyList);
+            pSection = CONTAINING_RECORD(ple, GDI_POOL_SECTION, leInUseLink);
+        }
+        else
+        {
+            /* No, allocate a new section */
+            pSection = GdiPoolAllocateSection(pPool);
+            if (!pSection)
+            {
+                DPRINT1("Couldn't allocate a section\n");
+                pvAlloc = NULL;
+                goto done;
+            }
+
+            /* Insert it into the ready list */
+            InsertHeadList(&pPool->leReadyList, &pSection->leReadyLink);
+        }
+
+        /* Insert it into the in-use list */
+        InsertHeadList(&pPool->leInUseList, &pSection->leInUseLink);
+    }
+
+    /* Find and set a single bit */
+    ulIndex = RtlFindClearBitsAndSet(&pSection->bitmap, 1, 0);
+    ASSERT(ulIndex != MAXULONG);
+
+    /* Calculate the allocation address */
+    cjOffset = ulIndex * pPool->cjAllocSize;
+    pvAlloc = (PVOID)((ULONG_PTR)pSection->pvBaseAddress + cjOffset);
+
+    /* Check if memory is comitted */
+    ulPageBit = 1 << (cjOffset / PAGE_SIZE);
+    ulPageBit |= 1 << ((cjOffset + pPool->cjAllocSize - 1) / PAGE_SIZE);
+    if ((pSection->ulCommitBitmap & ulPageBit) != ulPageBit)
+    {
+        /* Commit the pages */
+        pvBaseAddress = PAGE_ALIGN(pvAlloc);
+        cjSize = ADDRESS_AND_SIZE_TO_SPAN_PAGES(pvAlloc, pPool->cjAllocSize) * 
PAGE_SIZE;
+        status = ZwAllocateVirtualMemory(NtCurrentProcess(),
+                                         &pvBaseAddress,
+                                         0,
+                                         &cjSize,
+                                         MEM_COMMIT,
+                                         PAGE_READWRITE);
+
+        pSection->ulCommitBitmap |= ulPageBit;
+    }
+
+    /* Increase alloc count and check if section is now busy */
+    pSection->cAllocCount++;
+    if (pSection->cAllocCount == pPool->cSlotsPerSection)
+    {
+        /* Remove the section from the ready list */
+        RemoveEntryList(&pSection->leReadyLink);
+    }
+
+done:
+    /* Release the pool lock and enable APCs */
+    ExReleasePushLockExclusive(&pPool->pushlock);
+    KeLeaveCriticalRegion();
+DPRINT1("GdiPoolallocate: %p\n", pvAlloc);
+
+    return pvAlloc;
+}
+
+
+VOID
+NTAPI
+GdiPoolFree(
+    PGDI_POOL pPool,
+    PVOID pvAlloc)
+{
+    PLIST_ENTRY ple;
+    PGDI_POOL_SECTION pSection;
+    ULONG_PTR cjOffset;
+    ULONG ulIndex;
+DPRINT1("GdiPoolFree: %p\n", pvAlloc);
+
+    /* Disable APCs and acquire the pool lock */
+    KeEnterCriticalRegion();
+    ExAcquirePushLockExclusive(&pPool->pushlock);
+
+    /* Loop all used sections */
+    for (ple = pPool->leInUseList.Flink;
+         ple != &pPool->leInUseList;
+         ple = ple->Flink)
+    {
+        /* Get the pointer to the section */
+        pSection = CONTAINING_RECORD(ple, GDI_POOL_SECTION, leInUseLink);
+
+        /* Calculate offset */
+        cjOffset = (ULONG_PTR)pvAlloc - (ULONG_PTR)pSection->pvBaseAddress;
+
+        /* Check if the allocation is from this section */
+        if (cjOffset < pPool->cjSectionSize)
+        {
+            /* Calculate the index of the allocation */
+            ulIndex = cjOffset / pPool->cjAllocSize;
+
+            /* Mark it as free */
+            ASSERT(RtlTestBit(&pSection->bitmap, ulIndex) == TRUE);
+            RtlClearBit(&pSection->bitmap, ulIndex);
+
+            /* Decrease allocation count */
+            pSection->cAllocCount--;
+
+            /* Check if the section got valid now */
+            if (pSection->cAllocCount == pPool->cSlotsPerSection - 1)
+            {
+                /* Insert it into the ready list */
+                InsertTailList(&pPool->leReadyList, &pSection->leReadyLink);
+            }
+            /* Check if it got empty now */
+            else if (pSection->cAllocCount == 0)
+            {
+                /* Remove the section from the lists */
+                RemoveEntryList(&pSection->leInUseLink);
+                RemoveEntryList(&pSection->leReadyLink);
+
+                if (pPool->cEmptySections > 1)
+                {
+                    /* Delete the section */
+                    GdiPoolDeleteSection(pPool, pSection);
+                }
+                else
+                {
+                    /* Insert it into the empty list */
+                    InsertHeadList(&pPool->leEmptyList, 
&pSection->leInUseLink);
+                    pPool->cEmptySections++;
+                }
+            }
+
+            goto done;
+        }
+    }
+
+    ASSERT(FALSE);
+    // KeBugCheck()
+
+done:
+    /* Release the pool lock and enable APCs */
+    ExReleasePushLockExclusive(&pPool->pushlock);
+    KeLeaveCriticalRegion();
+}
+
+PGDI_POOL
+NTAPI
+GdiPoolCreate(
+    ULONG cjAllocSize,
+    ULONG ulTag)
+{
+    PGDI_POOL pPool;
+
+    /* Allocate a pool object */
+    pPool = EngAllocMem(0, sizeof(GDI_POOL), 'lopG');
+    if (!pPool) return NULL;
+
+    /* Initialize the object */
+    ExInitializePushLock(&pPool->pushlock);
+    InitializeListHead(&pPool->leInUseList);
+    InitializeListHead(&pPool->leReadyList);
+    InitializeListHead(&pPool->leEmptyList);
+    pPool->cEmptySections = 0;
+    pPool->cjAllocSize = cjAllocSize;
+    pPool->ulTag = ulTag;
+    pPool->cjSectionSize = GDI_POOL_ALLOCATION_GRANULARITY;
+    pPool->cSlotsPerSection = pPool->cjSectionSize / cjAllocSize;
+
+    return pPool;
+}
+
+VOID
+NTAPI
+GdiPoolDestroy(PGDI_POOL pPool)
+{
+    PGDI_POOL_SECTION pSection;
+    PLIST_ENTRY ple;
+
+    /* Loop all empty sections, removing them */
+    while ((ple = RemoveHeadList(&pPool->leEmptyList)))
+    {
+        /* Delete the section */
+        pSection = CONTAINING_RECORD(ple, GDI_POOL_SECTION, leInUseLink);
+        GdiPoolDeleteSection(pPool, pSection);
+    }
+
+    /* Loop all ready sections, removing them */
+    while ((ple = RemoveHeadList(&pPool->leInUseList)))
+    {
+        /* Delete the section */
+        pSection = CONTAINING_RECORD(ple, GDI_POOL_SECTION, leInUseLink);
+        GdiPoolDeleteSection(pPool, pSection);
+    }
+
+    EngFreeMem(pPool);
+}

Propchange: trunk/reactos/subsystems/win32/win32k/objects/gdipool.c
------------------------------------------------------------------------------
    svn:eol-style = native


Reply via email to