[Win32k]
[reactos.git] / reactos / subsystems / win32 / win32k / objects / gdiobj.c
index 55a805b..6bbca8c 100644 (file)
@@ -8,28 +8,16 @@
 
 /** INCLUDES ******************************************************************/
 
-//#define GDI_DEBUG
-
 #include <win32k.h>
 #define NDEBUG
 #include <debug.h>
 
-#define GDI_ENTRY_TO_INDEX(ht, e)                                              \
-  (((ULONG_PTR)(e) - (ULONG_PTR)&((ht)->Entries[0])) / sizeof(GDI_TABLE_ENTRY))
-#define GDI_HANDLE_GET_ENTRY(HandleTable, h)                                   \
-  (&(HandleTable)->Entries[GDI_HANDLE_GET_INDEX((h))])
-
-/* apparently the first 10 entries are never used in windows as they are empty */
-#define RESERVE_ENTRIES_COUNT 10
-
 #define BASE_OBJTYPE_COUNT 32
 
 #define DelayExecution() \
   DPRINT("%s:%i: Delay\n", __FILE__, __LINE__); \
   KeDelayExecutionThread(KernelMode, FALSE, &ShortDelay)
 
-#include "gdidbg.c"
-
 static
 BOOL INTERNAL_CALL GDI_CleanupDummy(PVOID ObjectBody);
 
@@ -272,64 +260,51 @@ ULONG
 FASTCALL
 InterlockedPopFreeEntry(VOID)
 {
-    ULONG idxFirst, idxNext, idxPrev;
+    ULONG iFirst, iNext, iPrev;
     PGDI_TABLE_ENTRY pEntry;
-    DWORD PrevProcId;
 
     DPRINT("Enter InterLockedPopFreeEntry\n");
 
-    while (TRUE)
+    do
     {
-        idxFirst = GdiHandleTable->FirstFree;
+        /* Get the index and sequence number of the first free entry */
+        iFirst = GdiHandleTable->FirstFree;
 
-        if (!idxFirst)
+        /* Check if we have a free entry */
+        if (!(iFirst & GDI_HANDLE_INDEX_MASK))
         {
             /* Increment FirstUnused and get the new index */
-            idxFirst = InterlockedIncrement((LONG*)&GdiHandleTable->FirstUnused) - 1;
+            iFirst = InterlockedIncrement((LONG*)&GdiHandleTable->FirstUnused) - 1;
 
-            /* Check if we have entries left */
-            if (idxFirst >= GDI_HANDLE_COUNT)
+            /* Check if we have unused entries left */
+            if (iFirst >= GDI_HANDLE_COUNT)
             {
                 DPRINT1("No more gdi handles left!\n");
                 return 0;
             }
 
             /* Return the old index */
-            return idxFirst;
+            return iFirst;
         }
 
         /* Get a pointer to the first free entry */
-        pEntry = GdiHandleTable->Entries + idxFirst;
+        pEntry = GdiHandleTable->Entries + (iFirst & GDI_HANDLE_INDEX_MASK);
 
-        /* Try to lock the entry */
-        PrevProcId = InterlockedCompareExchange((LONG*)&pEntry->ProcessId, 1, 0);
-        if (PrevProcId != 0)
-        {
-            /* The entry was locked or not free, wait and start over */
-            DelayExecution();
-            continue;
-        }
-
-        /* Sanity check: is entry really free? */
-        ASSERT(((ULONG_PTR)pEntry->KernelData & ~GDI_HANDLE_INDEX_MASK) == 0);
+        /* Create a new value with an increased sequence number */
+        iNext = (USHORT)(ULONG_PTR)pEntry->KernelData;
+        iNext |= (iFirst & ~GDI_HANDLE_INDEX_MASK) + 0x10000;
 
         /* Try to exchange the FirstFree value */
-        idxNext = (ULONG_PTR)pEntry->KernelData;
-        idxPrev = InterlockedCompareExchange((LONG*)&GdiHandleTable->FirstFree,
-                                             idxNext,
-                                             idxFirst);
-
-        /* Unlock the free entry */
-        (void)InterlockedExchange((LONG*)&pEntry->ProcessId, 0);
-
-        /* If we succeeded, break out of the loop */
-        if (idxPrev == idxFirst)
-        {
-            break;
-        }
+        iPrev = InterlockedCompareExchange((LONG*)&GdiHandleTable->FirstFree,
+                                           iNext,
+                                           iFirst);
     }
+    while (iPrev != iFirst);
+
+    /* Sanity check: is entry really free? */
+    ASSERT(((ULONG_PTR)pEntry->KernelData & ~GDI_HANDLE_INDEX_MASK) == 0);
 
-    return idxFirst;
+    return iFirst & GDI_HANDLE_INDEX_MASK;
 }
 
 /* Pushes an entry of the handle table to the free list,
@@ -338,7 +313,7 @@ VOID
 FASTCALL
 InterlockedPushFreeEntry(ULONG idxToFree)
 {
-    ULONG idxFirstFree, idxPrev;
+    ULONG iToFree, iFirst, iPrev;
     PGDI_TABLE_ENTRY pFreeEntry;
 
     DPRINT("Enter InterlockedPushFreeEntry\n");
@@ -346,18 +321,26 @@ InterlockedPushFreeEntry(ULONG idxToFree)
     pFreeEntry = GdiHandleTable->Entries + idxToFree;
     ASSERT((pFreeEntry->Type & GDI_ENTRY_BASETYPE_MASK) == 0);
     ASSERT(pFreeEntry->ProcessId == 0);
-    pFreeEntry->UserData = NULL;
+    pFreeEntry->UserData = NULL; // FIXME
+    ASSERT(pFreeEntry->UserData == NULL);
 
     do
     {
-        idxFirstFree = GdiHandleTable->FirstFree;
-        pFreeEntry->KernelData = (PVOID)(ULONG_PTR)idxFirstFree;
+        /* Get the current first free index and sequence number */
+        iFirst = GdiHandleTable->FirstFree;
+
+        /* Set the KernelData member to the index of the first free entry */
+        pFreeEntry->KernelData = UlongToPtr(iFirst & GDI_HANDLE_INDEX_MASK);
 
-        idxPrev = InterlockedCompareExchange((LONG*)&GdiHandleTable->FirstFree,
-                                             idxToFree,
-                                             idxFirstFree);
+        /* Combine new index and increased sequence number in iToFree */
+        iToFree = idxToFree | ((iFirst & ~GDI_HANDLE_INDEX_MASK) + 0x10000);
+
+        /* Try to atomically update the first free entry */
+        iPrev = InterlockedCompareExchange((LONG*)&GdiHandleTable->FirstFree,
+                                           iToFree,
+                                           iFirst);
     }
-    while (idxPrev != idxFirstFree);
+    while (iPrev != iFirst);
 }
 
 
@@ -492,6 +475,9 @@ LockHandle:
             newObject->ulShareCount = 0;
             newObject->cExclusiveLock = 1;
             newObject->Tid = Thread;
+#if DBG
+            if (Thread) Thread->cExclusiveLocks++;
+#endif
 
             AllocTypeDataDump(TypeInfo);
 
@@ -612,11 +598,11 @@ LockHandle:
              ((Entry->Type & GDI_ENTRY_BASETYPE_MASK) == (HandleUpper & GDI_ENTRY_BASETYPE_MASK)) )
         {
             POBJ Object;
+            PTHREADINFO Thread = (PTHREADINFO)PsGetCurrentThreadWin32Thread();
 
             Object = Entry->KernelData;
 
-            if ((Object->cExclusiveLock == 0 ||
-                Object->Tid == (PTHREADINFO)PsGetCurrentThreadWin32Thread()) &&
+            if ((Object->cExclusiveLock == 0 || Object->Tid == Thread) &&
                  Object->ulShareCount == 0)
             {
                 BOOL Ret;
@@ -632,6 +618,18 @@ LockHandle:
                 InterlockedPushFreeEntry(GDI_ENTRY_TO_INDEX(GdiHandleTable, Entry));
 
                 Object->hHmgr = NULL;
+#if DBG
+                if (Thread)
+                {
+                    if (Thread->cExclusiveLocks < Object->cExclusiveLock)
+                    {
+                        DPRINT1("cExclusiveLocks = %ld, object: %ld\n",
+                                Thread->cExclusiveLocks, Object->cExclusiveLock);
+                        ASSERT(FALSE);
+                    }
+                    Thread->cExclusiveLocks -= Object->cExclusiveLock;
+                }
+#endif
 
                 if (W32Process != NULL)
                 {
@@ -1014,16 +1012,6 @@ GDIOBJ_LockObj(HGDIOBJ hObj, DWORD ExpectedType)
     }
 
     ProcessId = (HANDLE)((ULONG_PTR)PsGetCurrentProcessId() & ~1);
-    HandleProcessId = (HANDLE)((ULONG_PTR)Entry->ProcessId & ~1);
-
-    /* Check for invalid owner. */
-    if (ProcessId != HandleProcessId && HandleProcessId != NULL)
-    {
-        DPRINT1("Tried to lock object (0x%p) of wrong owner! ProcessId = %p, HandleProcessId = %p\n", hObj, ProcessId, HandleProcessId);
-        GDIDBG_TRACECALLER();
-        GDIDBG_TRACEALLOCATOR(hObj);
-        return NULL;
-    }
 
     /*
      * Prevent the thread from being terminated during the locking process.
@@ -1040,6 +1028,17 @@ GDIOBJ_LockObj(HGDIOBJ hObj, DWORD ExpectedType)
 
     for (;;)
     {
+        HandleProcessId = (HANDLE)((ULONG_PTR)Entry->ProcessId & ~1);
+
+        /* Check for invalid owner. */
+        if (ProcessId != HandleProcessId && HandleProcessId != NULL)
+        {
+            DPRINT1("Tried to lock object (0x%p) of wrong owner! ProcessId = %p, HandleProcessId = %p\n", hObj, ProcessId, HandleProcessId);
+            GDIDBG_TRACECALLER();
+            GDIDBG_TRACEALLOCATOR(hObj);
+            break;
+        }
+
         /* Lock the handle table entry. */
         LockedProcessId = (HANDLE)((ULONG_PTR)HandleProcessId | 0x1);
         PrevProcId = InterlockedCompareExchangePointer((PVOID*)&Entry->ProcessId,
@@ -1064,6 +1063,9 @@ GDIOBJ_LockObj(HGDIOBJ hObj, DWORD ExpectedType)
                     Object->Tid = Thread;
                     Object->cExclusiveLock = 1;
                     GDIDBG_CAPTURELOCKER(GDI_HANDLE_GET_INDEX(hObj))
+#if DBG
+                    if (Thread) Thread->cExclusiveLocks++;
+#endif
                 }
                 else
                 {
@@ -1076,6 +1078,9 @@ GDIOBJ_LockObj(HGDIOBJ hObj, DWORD ExpectedType)
                         continue;
                     }
                     InterlockedIncrement((PLONG)&Object->cExclusiveLock);
+#if DBG
+                     if (Thread) Thread->cExclusiveLocks++;
+#endif
                 }
             }
             else