dump some statistics on the gdi handle table when it runs out of handles
[reactos.git] / reactos / subsys / win32k / objects / gdiobj.c
index a20bbcd..7e3dd4c 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ReactOS W32 Subsystem
- *  Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team
+ *  Copyright (C) 1998 - 2004 ReactOS Team
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
 /*
  * GDIOBJ.C - GDI object manipulation routines
  *
- * $Id: gdiobj.c,v 1.64 2004/04/03 20:33:39 gvg Exp $
- *
+ * $Id: gdiobj.c,v 1.79 2004/12/18 21:41:17 royce Exp $
  */
+#include <w32k.h>
 
-#undef WIN32_LEAN_AND_MEAN
-#define WIN32_NO_STATUS
-#include <windows.h>
-#include <ddk/ntddk.h>
-#include <include/dce.h>
-#include <include/object.h>
-#include <win32k/gdiobj.h>
-#include <win32k/brush.h>
-#include <win32k/pen.h>
-#include <win32k/text.h>
-#include <win32k/dc.h>
-#include <win32k/bitmaps.h>
-#include <win32k/region.h>
-#include <win32k/cursoricon.h>
-#include <include/palette.h>
-#include <include/intgdi.h>
-#include <include/tags.h>
 #define NDEBUG
-#include <win32k/debug1.h>
+#include <debug.h>
 
-/* count all gdi objects */
-#define GDI_COUNT_OBJECTS 1
+#ifdef __USE_W32API
+/* F*(&#$ header mess!!!! */
+HANDLE
+STDCALL PsGetProcessId(
+       PEPROCESS       Process
+       );
+#endif /* __USE_W32API */
 
-/*! Size of the GDI handle table
- * http://www.windevnet.com/documents/s=7290/wdj9902b/9902b.htm
- * gdi handle table can hold 0x4000 handles
-*/
-#define GDI_HANDLE_COUNT 0x4000
-
-#define GDI_GLOBAL_PROCESS ((HANDLE) 0xffffffff)
-
-#define GDI_HANDLE_INDEX_MASK (GDI_HANDLE_COUNT - 1)
-#define GDI_HANDLE_TYPE_MASK  0x007f0000
-#define GDI_HANDLE_STOCK_MASK 0x00800000
-
-#define GDI_HANDLE_CREATE(i, t)    ((HANDLE)(((i) & GDI_HANDLE_INDEX_MASK) | ((t) & GDI_HANDLE_TYPE_MASK)))
-#define GDI_HANDLE_GET_INDEX(h)    (((DWORD)(h)) & GDI_HANDLE_INDEX_MASK)
-#define GDI_HANDLE_GET_TYPE(h)     (((DWORD)(h)) & GDI_HANDLE_TYPE_MASK)
-#define GDI_HANDLE_IS_TYPE(h, t)   ((t) == (((DWORD)(h)) & GDI_HANDLE_TYPE_MASK))
-#define GDI_HANDLE_IS_STOCKOBJ(h)  (0 != (((DWORD)(h)) & GDI_HANDLE_STOCK_MASK))
-#define GDI_HANDLE_SET_STOCKOBJ(h) ((h) = (HANDLE)(((DWORD)(h)) | GDI_HANDLE_STOCK_MASK))
-
-#define GDI_TYPE_TO_MAGIC(t) ((WORD) ((t) >> 16))
-#define GDI_MAGIC_TO_TYPE(m) ((DWORD)(m) << 16)
-
-/* FIXME Ownership of GDI objects by processes not properly implemented yet */
-#if 0
-#define GDI_VALID_OBJECT(h, obj, t, f) \
-  (NULL != (obj) \
-   && (GDI_MAGIC_TO_TYPE((obj)->Magic) == (t) || GDI_OBJECT_TYPE_DONTCARE == (t)) \
-   && (GDI_HANDLE_GET_TYPE((h)) == GDI_MAGIC_TO_TYPE((obj)->Magic)) \
-   && (((obj)->hProcessId == PsGetCurrentProcessId()) \
-       || (GDI_GLOBAL_PROCESS == (obj)->hProcessId) \
-       || ((f) & GDIOBJFLAG_IGNOREPID)))
-#else
-#define GDI_VALID_OBJECT(h, obj, t, f) \
-  (NULL != (obj) \
-   && (GDI_MAGIC_TO_TYPE((obj)->Magic) == (t) || GDI_OBJECT_TYPE_DONTCARE == (t)) \
-   && (GDI_HANDLE_GET_TYPE((h)) == GDI_MAGIC_TO_TYPE((obj)->Magic)))
-#endif
+
+
+
+#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))])
+
+#define GDIBdyToHdr(body)                                                      \
+  ((PGDIOBJHDR)(body) - 1)
+#define GDIHdrToBdy(hdr)                                                       \
+  (PGDIOBJ)((PGDIOBJHDR)(hdr) + 1)
+
+/* apparently the first 10 entries are never used in windows as they are empty */
+#define RESERVE_ENTRIES_COUNT 10
 
 typedef struct _GDI_HANDLE_TABLE
 {
-  WORD wTableSize;
-  WORD AllocationHint;
-  #if GDI_COUNT_OBJECTS
-  ULONG HandlesCount;
-  #endif
   PPAGED_LOOKASIDE_LIST LookasideLists;
-  PGDIOBJHDR Handles[1];
+
+  SLIST_HEADER FreeEntriesHead;
+  SLIST_ENTRY FreeEntries[((GDI_HANDLE_COUNT * sizeof(GDI_TABLE_ENTRY)) << 3) /
+                          (sizeof(SLIST_ENTRY) << 3)];
+
+  GDI_TABLE_ENTRY Entries[GDI_HANDLE_COUNT];
 } GDI_HANDLE_TABLE, *PGDI_HANDLE_TABLE;
 
 typedef struct
 {
   ULONG Type;
   ULONG Size;
-} GDI_OBJ_SIZE;
+  GDICLEANUPPROC CleanupProc;
+} GDI_OBJ_INFO, *PGDI_OBJ_INFO;
+
+/*
+ * Dummy GDI Cleanup Callback
+ */
+BOOL INTERNAL_CALL
+GDI_CleanupDummy(PVOID ObjectBody)
+{
+  return TRUE;
+}
 
+/* Testing shows that regions are the most used GDIObj type,
+   so put that one first for performance */
 const
-GDI_OBJ_SIZE ObjSizes[] =
+GDI_OBJ_INFO ObjInfo[] =
 {
-  /* Testing shows that regions are the most used GDIObj type,
-     so put that one first for performance */
-  {GDI_OBJECT_TYPE_REGION,      sizeof(ROSRGNDATA)},
-  {GDI_OBJECT_TYPE_BITMAP,      sizeof(BITMAPOBJ)},
-  {GDI_OBJECT_TYPE_DC,          sizeof(DC)},
-  {GDI_OBJECT_TYPE_PALETTE,     sizeof(PALGDI)},
-  {GDI_OBJECT_TYPE_BRUSH,       sizeof(BRUSHOBJ)},
-  {GDI_OBJECT_TYPE_PEN,         sizeof(PENOBJ)},
-  {GDI_OBJECT_TYPE_FONT,        sizeof(TEXTOBJ)},
-  {GDI_OBJECT_TYPE_DCE,         sizeof(DCE)},
-/*
-  {GDI_OBJECT_TYPE_DIRECTDRAW,  sizeof(DD_DIRECTDRAW)},
-  {GDI_OBJECT_TYPE_DD_SURFACE,  sizeof(DD_SURFACE)},
-*/
-  {GDI_OBJECT_TYPE_EXTPEN,      0},
-  {GDI_OBJECT_TYPE_METADC,      0},
-  {GDI_OBJECT_TYPE_METAFILE,    0},
-  {GDI_OBJECT_TYPE_ENHMETAFILE, 0},
-  {GDI_OBJECT_TYPE_ENHMETADC,   0},
-  {GDI_OBJECT_TYPE_MEMDC,       0},
-  {GDI_OBJECT_TYPE_EMF,         0}
+   /* Type */                   /* Size */             /* CleanupProc */
+  {GDI_OBJECT_TYPE_REGION,      sizeof(ROSRGNDATA),    RGNDATA_Cleanup},
+  {GDI_OBJECT_TYPE_BITMAP,      sizeof(BITMAPOBJ),     BITMAP_Cleanup},
+  {GDI_OBJECT_TYPE_DC,          sizeof(DC),            DC_Cleanup},
+  {GDI_OBJECT_TYPE_PALETTE,     sizeof(PALGDI),        PALETTE_Cleanup},
+  {GDI_OBJECT_TYPE_BRUSH,       sizeof(GDIBRUSHOBJ),   BRUSH_Cleanup},
+  {GDI_OBJECT_TYPE_PEN,         sizeof(GDIBRUSHOBJ),   GDI_CleanupDummy},
+  {GDI_OBJECT_TYPE_FONT,        sizeof(TEXTOBJ),       GDI_CleanupDummy},
+  {GDI_OBJECT_TYPE_DCE,         sizeof(DCE),           DCE_Cleanup},
+/*{GDI_OBJECT_TYPE_DIRECTDRAW,  sizeof(DD_DIRECTDRAW), DD_Cleanup},
+  {GDI_OBJECT_TYPE_DD_SURFACE,  sizeof(DD_SURFACE),    DDSURF_Cleanup},*/
+  {GDI_OBJECT_TYPE_EXTPEN,      0,                     GDI_CleanupDummy},
+  {GDI_OBJECT_TYPE_METADC,      0,                     GDI_CleanupDummy},
+  {GDI_OBJECT_TYPE_METAFILE,    0,                     GDI_CleanupDummy},
+  {GDI_OBJECT_TYPE_ENHMETAFILE, 0,                     GDI_CleanupDummy},
+  {GDI_OBJECT_TYPE_ENHMETADC,   0,                     GDI_CleanupDummy},
+  {GDI_OBJECT_TYPE_MEMDC,       0,                     GDI_CleanupDummy},
+  {GDI_OBJECT_TYPE_EMF,         0,                     GDI_CleanupDummy}
 };
 
-#define OBJTYPE_COUNT (sizeof(ObjSizes) / sizeof(ObjSizes[0]))
-
-/*  GDI stock objects */
-
-static LOGBRUSH WhiteBrush =
-{ BS_SOLID, RGB(255,255,255), 0 };
-
-static LOGBRUSH LtGrayBrush =
-/* FIXME : this should perhaps be BS_HATCHED, at least for 1 bitperpixel */
-{ BS_SOLID, RGB(192,192,192), 0 };
-
-static LOGBRUSH GrayBrush =
-/* FIXME : this should perhaps be BS_HATCHED, at least for 1 bitperpixel */
-{ BS_SOLID, RGB(128,128,128), 0 };
-
-static LOGBRUSH DkGrayBrush =
-/* This is BS_HATCHED, for 1 bitperpixel. This makes the spray work in pbrush */
-/* NB_HATCH_STYLES is an index into HatchBrushes */
-{ BS_HATCHED, RGB(0,0,0), NB_HATCH_STYLES };
-
-static LOGBRUSH BlackBrush =
-{ BS_SOLID, RGB(0,0,0), 0 };
-
-static LOGBRUSH NullBrush =
-{ BS_NULL, 0, 0 };
-
-static LOGPEN WhitePen =
-{ PS_SOLID, { 0, 0 }, RGB(255,255,255) };
-
-static LOGPEN BlackPen =
-{ PS_SOLID, { 0, 0 }, RGB(0,0,0) };
-
-static LOGPEN NullPen =
-{ PS_NULL, { 0, 0 }, 0 };
-
-static LOGFONTW OEMFixedFont =
-{ 11, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, OEM_CHARSET,
-  0, 0, DEFAULT_QUALITY, FIXED_PITCH | FF_MODERN, L"Bitstream Vera Sans Mono" };
-
-static LOGFONTW AnsiFixedFont =
-{ 11, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET,
-  0, 0, DEFAULT_QUALITY, FIXED_PITCH | FF_MODERN, L"Bitstream Vera Sans Mono" };
+#define OBJTYPE_COUNT (sizeof(ObjInfo) / sizeof(ObjInfo[0]))
 
-/*static LOGFONTW AnsiVarFont =
- *{ 10, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET,
- *  0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS, L"MS Sans Serif" }; */
+static PGDI_HANDLE_TABLE HandleTable = NULL;
+static LARGE_INTEGER ShortDelay;
 
-static LOGFONTW SystemFont =
-{ 11, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET,
-  0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS, L"Bitstream Vera Sans" };
-
-static LOGFONTW DeviceDefaultFont =
-{ 11, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET,
-  0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS, L"Bitstream Vera Sans" };
-
-static LOGFONTW SystemFixedFont =
-{ 11, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET,
-  0, 0, DEFAULT_QUALITY, FIXED_PITCH | FF_MODERN, L"Bitstream Vera Sans Mono" };
-
-/* FIXME: Is this correct? */
-static LOGFONTW DefaultGuiFont =
-{ 11, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET,
-  0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS, L"Bitstream Vera Sans" };
-
-#define NB_STOCK_OBJECTS (DEFAULT_GUI_FONT + 1)
-
-static HGDIOBJ *StockObjects[NB_STOCK_OBJECTS];
-static PGDI_HANDLE_TABLE  HandleTable = 0;
-static FAST_MUTEX  HandleTableMutex;
-static FAST_MUTEX  RefCountHandling;
+#define DelayExecution() \
+  DPRINT("%s:%i: Delay\n", __FILE__, __LINE__); \
+  KeDelayExecutionThread(KernelMode, FALSE, &ShortDelay)
 
 /*!
  * Allocate GDI object table.
  * \param      Size - number of entries in the object table.
- * Notes:: Must be called at IRQL < DISPATCH_LEVEL.
 */
-static PGDI_HANDLE_TABLE FASTCALL
-GDIOBJ_iAllocHandleTable (WORD Size)
+static PGDI_HANDLE_TABLE INTERNAL_CALL
+GDIOBJ_iAllocHandleTable(VOID)
 {
-  PGDI_HANDLE_TABLE  handleTable;
-  ULONG MemSize;
+  PGDI_HANDLE_TABLE handleTable;
   UINT ObjType;
-  
-  MemSize = sizeof(GDI_HANDLE_TABLE) + sizeof(PGDIOBJ) * Size;
+  UINT i;
+  PGDI_TABLE_ENTRY Entry;
 
-  /* prevent APC delivery for the *FastMutexUnsafe calls */
-  const KIRQL PrevIrql = KfRaiseIrql(APC_LEVEL);
-  ExAcquireFastMutexUnsafe (&HandleTableMutex);
-  handleTable = ExAllocatePoolWithTag(PagedPool, MemSize, TAG_GDIHNDTBLE);
+  handleTable = ExAllocatePoolWithTag(NonPagedPool, sizeof(GDI_HANDLE_TABLE), TAG_GDIHNDTBLE);
   ASSERT( handleTable );
-  memset (handleTable, 0, MemSize);
-#if GDI_COUNT_OBJECTS
-  handleTable->HandlesCount = 0;
-#endif
-  handleTable->wTableSize = Size;
-  handleTable->AllocationHint = 1;
+  RtlZeroMemory(handleTable, sizeof(GDI_HANDLE_TABLE));
+
+  /*
+   * initialize the free entry cache
+   */
+  InitializeSListHead(&handleTable->FreeEntriesHead);
+  Entry = &HandleTable->Entries[RESERVE_ENTRIES_COUNT];
+  for(i = GDI_HANDLE_COUNT - 1; i >= RESERVE_ENTRIES_COUNT; i--)
+  {
+    InterlockedPushEntrySList(&handleTable->FreeEntriesHead, &handleTable->FreeEntries[i]);
+  }
+
   handleTable->LookasideLists = ExAllocatePoolWithTag(NonPagedPool,
                                                       OBJTYPE_COUNT * sizeof(PAGED_LOOKASIDE_LIST),
                                                       TAG_GDIHNDTBLE);
-  if (NULL == handleTable->LookasideLists)
-    {
-      ExFreePool(handleTable);
-      ExReleaseFastMutexUnsafe (&HandleTableMutex);
-      KfLowerIrql(PrevIrql);
-      return NULL;
-    }
-  for (ObjType = 0; ObjType < OBJTYPE_COUNT; ObjType++)
-    {
-      ExInitializePagedLookasideList(handleTable->LookasideLists + ObjType, NULL, NULL, 0,
-                                     ObjSizes[ObjType].Size + sizeof(GDIOBJHDR), TAG_GDIOBJ, 0);
-    }
-  ExReleaseFastMutexUnsafe (&HandleTableMutex);
-  KfLowerIrql(PrevIrql);
+  if(handleTable->LookasideLists == NULL)
+  {
+    ExFreePool(handleTable);
+    return NULL;
+  }
+
+  for(ObjType = 0; ObjType < OBJTYPE_COUNT; ObjType++)
+  {
+    ExInitializePagedLookasideList(handleTable->LookasideLists + ObjType, NULL, NULL, 0,
+                                   ObjInfo[ObjType].Size + sizeof(GDIOBJHDR), TAG_GDIOBJ, 0);
+  }
+
+  ShortDelay.QuadPart = -5000LL; /* FIXME - 0.5 ms? */
 
   return handleTable;
 }
 
-/*!
- * Returns the entry into the handle table by index.
-*/
-static PGDIOBJHDR FASTCALL
-GDIOBJ_iGetObjectForIndex(WORD TableIndex)
+static inline PPAGED_LOOKASIDE_LIST
+FindLookasideList(DWORD ObjectType)
 {
-  if (0 == TableIndex || HandleTable->wTableSize < TableIndex)
+  int Index;
+
+  for (Index = 0; Index < OBJTYPE_COUNT; Index++)
+  {
+    if (ObjInfo[Index].Type == ObjectType)
     {
-      DPRINT1("Invalid TableIndex %u\n", (unsigned) TableIndex);
-      return NULL;
+      return HandleTable->LookasideLists + Index;
     }
+  }
+
+  DPRINT1("Can't find lookaside list for object type 0x%08x\n", ObjectType);
 
-  return HandleTable->Handles[TableIndex];
+  return NULL;
 }
 
-/*!
- * Finds next free entry in the GDI handle table.
- * \return     index into the table is successful, zero otherwise.
-*/
-static WORD FASTCALL
-GDIOBJ_iGetNextOpenHandleIndex (void)
+static inline BOOL
+RunCleanupCallback(PGDIOBJ pObj, DWORD ObjectType)
 {
-   WORD tableIndex;
-
-   for (tableIndex = HandleTable->AllocationHint;
-        tableIndex < HandleTable->wTableSize;
-        tableIndex++)
-   {
-      if (HandleTable->Handles[tableIndex] == NULL)
-      {
-         HandleTable->AllocationHint = tableIndex + 1;
-         return tableIndex;
-      }
-   }
+  int Index;
 
-   for (tableIndex = 1;
-        tableIndex < HandleTable->AllocationHint;
-        tableIndex++)
-   {
-      if (HandleTable->Handles[tableIndex] == NULL)
-      {
-         HandleTable->AllocationHint = tableIndex + 1;
-         return tableIndex;
-      }
-   }
+  for (Index = 0; Index < OBJTYPE_COUNT; Index++)
+  {
+    if (ObjInfo[Index].Type == ObjectType)
+    {
+      return ((GDICLEANUPPROC)ObjInfo[Index].CleanupProc)(pObj);
+    }
+  }
 
-   return 0;
+  DPRINT1("Can't find cleanup callback for object type 0x%08x\n", ObjectType);
+  return TRUE;
 }
 
-static PPAGED_LOOKASIDE_LIST FASTCALL
-FindLookasideList(DWORD ObjectType)
+static inline ULONG
+GetObjectSize(DWORD ObjectType)
 {
   int Index;
 
   for (Index = 0; Index < OBJTYPE_COUNT; Index++)
+  {
+    if (ObjInfo[Index].Type == ObjectType)
     {
-      if (ObjSizes[Index].Type == ObjectType)
-        {
-          return HandleTable->LookasideLists + Index;
-        }
+      return ObjInfo[Index].Size;
     }
+  }
 
-  DPRINT1("Can't find lookaside list for object type 0x%08x\n", ObjectType);
+  DPRINT1("Can't find size for object type 0x%08x\n", ObjectType);
+  return 0;
+}
 
-  return NULL;
+#ifdef DBG
+
+static int leak_reported = 0;
+#define GDI_STACK_LEVELS 4
+static ULONG GDIHandleAllocator[GDI_STACK_LEVELS][GDI_HANDLE_COUNT];
+struct DbgOpenGDIHandle
+{
+       ULONG loc;
+       int count;
+};
+#define H 1024
+static struct DbgOpenGDIHandle h[H];
+
+void IntDumpHandleTable ( int which )
+{
+       int i, n = 0, j;
+
+       // step through GDI handle table and find out who our culprit is...
+       for ( i = RESERVE_ENTRIES_COUNT; i < GDI_HANDLE_COUNT; i++ )
+       {
+               for ( j = 0; j < n; j++ )
+               {
+                       if ( GDIHandleAllocator[which][i] == h[j].loc )
+                               break;
+               }
+               if ( j < H )
+               {
+                       if ( j == n )
+                       {
+                               h[j].loc = GDIHandleAllocator[which][i];
+                               h[j].count = 1;
+                               n = n + 1;
+                       }
+                       else
+                               h[j].count++;
+               }
+       }
+       // bubble sort time! weeeeee!!
+       for ( i = 0; i < n-1; i++ )
+       {
+               if ( h[i].count < h[i+1].count )
+               {
+                       struct DbgOpenGDIHandle t;
+                       t.loc = h[i+1].loc;
+                       t.count = h[i+1].count;
+                       h[i+1].loc = h[i].loc;
+                       h[i+1].count = h[i].count;
+                       j = i;
+                       while ( j > 0 && h[j-1].count < t.count )
+                               j--;
+                       h[j] = t;
+               }
+       }
+       // print the first 30 offenders...
+       DbgPrint ( "Worst GDI Handle leak offenders - stack trace level %i (out of %i unique locations):\n", which, n );
+       for ( i = 0; i < 30 && i < n; i++ )
+       {
+               DbgPrint ( "\t" );
+               if ( !KeRosPrintAddress ( (PVOID)h[i].loc ) )
+                       DbgPrint ( "<%X>", h[i].loc );
+               DbgPrint ( " (%i allocations)\n", h[i].count );
+       }
 }
+#endif//DBG
 
 /*!
  * Allocate memory for GDI object and return handle to it.
  *
- * \param Size - size of the GDI object. This shouldn't to include the size of GDIOBJHDR.
- * The actual amount of allocated memory is sizeof(GDIOBJHDR)+Size
  * \param ObjectType - type of object \ref GDI object types
- * \param CleanupProcPtr - Routine to be called on destruction of object
  *
  * \return Handle of the allocated object.
  *
  * \note Use GDIOBJ_Lock() to obtain pointer to the new object.
+ * \todo return the object pointer and lock it by default.
 */
-HGDIOBJ FASTCALL
-GDIOBJ_AllocObj(WORD Size, DWORD ObjectType, GDICLEANUPPROC CleanupProc)
+HGDIOBJ INTERNAL_CALL
+#ifdef GDI_DEBUG
+GDIOBJ_AllocObjDbg(const char* file, int line, ULONG ObjectType)
+#else /* !GDI_DEBUG */
+GDIOBJ_AllocObj(ULONG ObjectType)
+#endif /* GDI_DEBUG */
 {
   PW32PROCESS W32Process;
   PGDIOBJHDR  newObject;
-  WORD Index;
   PPAGED_LOOKASIDE_LIST LookasideList;
+  LONG CurrentProcessId, LockedProcessId;
+#ifdef GDI_DEBUG
+  ULONG Attempts = 0;
+#endif
 
-  ExAcquireFastMutex(&HandleTableMutex);
-  Index = GDIOBJ_iGetNextOpenHandleIndex ();
-  if (0 == Index)
-    {
-      ExReleaseFastMutex(&HandleTableMutex);
-      DPRINT1("Out of GDI handles\n");
-      return NULL;
-    }
+  ASSERT(ObjectType != GDI_OBJECT_TYPE_DONTCARE);
 
   LookasideList = FindLookasideList(ObjectType);
-  if (NULL == LookasideList)
-    {
-      ExReleaseFastMutex(&HandleTableMutex);
-      return NULL;
-    }
-  newObject = ExAllocateFromPagedLookasideList(LookasideList);
-  if (NULL == newObject)
+  if(LookasideList != NULL)
+  {
+    newObject = ExAllocateFromPagedLookasideList(LookasideList);
+    if(newObject != NULL)
     {
-      ExReleaseFastMutex(&HandleTableMutex);
-      DPRINT1("Unable to allocate GDI object from lookaside list\n");
-      return NULL;
-    }
-  RtlZeroMemory (newObject, Size + sizeof(GDIOBJHDR));
+      PSLIST_ENTRY FreeEntry;
+      PGDI_TABLE_ENTRY Entry;
+      PGDIOBJ ObjectBody;
+      LONG TypeInfo;
+
+      /* shift the process id to the left so we can use the first bit to lock
+         the object.
+         FIXME - don't shift once ROS' PIDs match with nt! */
+      CurrentProcessId = (LONG)PsGetCurrentProcessId() << 1;
+      LockedProcessId = CurrentProcessId | 0x1;
+      W32Process = PsGetWin32Process();
+
+      newObject->LockingThread = NULL;
+      newObject->Locks = 0;
+
+#ifdef GDI_DEBUG
+      newObject->createdfile = file;
+      newObject->createdline = line;
+      newObject->lockfile = NULL;
+      newObject->lockline = 0;
+#endif
+
+      ObjectBody = GDIHdrToBdy(newObject);
+
+      RtlZeroMemory(ObjectBody, GetObjectSize(ObjectType));
+
+      TypeInfo = (ObjectType & 0xFFFF0000) | (ObjectType >> 16);
 
-  newObject->wTableIndex = Index;
+      FreeEntry = InterlockedPopEntrySList(&HandleTable->FreeEntriesHead);
+      if(FreeEntry != NULL)
+      {
+        LONG PrevProcId;
+        UINT Index;
+        HGDIOBJ Handle;
+
+        /* calculate the entry from the address of the entry in the free slot array */
+        Index = ((ULONG_PTR)FreeEntry - (ULONG_PTR)&HandleTable->FreeEntries[0]) /
+                sizeof(HandleTable->FreeEntries[0]);
+        Entry = &HandleTable->Entries[Index];
+        Handle = (HGDIOBJ)((Index & 0xFFFF) | (ObjectType & 0xFFFF0000));
+
+LockHandle:
+        PrevProcId = InterlockedCompareExchange(&Entry->ProcessId, LockedProcessId, 0);
+        if(PrevProcId == 0)
+        {
+          ASSERT(Entry->KernelData == NULL);
+
+          Entry->KernelData = ObjectBody;
+
+          /* we found a free entry, no need to exchange this field atomically
+             since we're holding the lock */
+          Entry->Type = TypeInfo;
 
-  newObject->dwCount = 0;
-  newObject->hProcessId = PsGetCurrentProcessId ();
-  newObject->CleanupProc = CleanupProc;
-  newObject->Magic = GDI_TYPE_TO_MAGIC(ObjectType);
-  newObject->lockfile = NULL;
-  newObject->lockline = 0;
-  ExInitializeFastMutex(&newObject->Lock);
-  HandleTable->Handles[Index] = newObject;
-#if GDI_COUNT_OBJECTS
-  HandleTable->HandlesCount++;
+          /* unlock the entry */
+          InterlockedExchange(&Entry->ProcessId, CurrentProcessId);
+
+#ifdef DBG
+          {
+            PULONG Frame;
+                       int which;
+#if defined __GNUC__
+            __asm__("mov %%ebp, %%ebx" : "=b" (Frame) : );
+#elif defined(_MSC_VER)
+            __asm mov [Frame], ebp
 #endif
-  ExReleaseFastMutex(&HandleTableMutex);
-  
-  W32Process = PsGetCurrentProcess()->Win32Process;
-  if(W32Process)
+                       Frame = (PULONG)Frame[0]; // step out of AllocObj()
+                       for ( which = 0; which < GDI_STACK_LEVELS && Frame[1] != 0 && Frame[1] != 0xDEADBEEF; which++ )
+                       {
+                   GDIHandleAllocator[which][Index] = Frame[1]; // step out of AllocObj()
+                               Frame = ((PULONG)Frame[0]);
+                       }
+                       for ( ; which < GDI_STACK_LEVELS; which++ )
+                               GDIHandleAllocator[which][Index] = 0xDEADBEEF;
+          }
+#endif//DBG
+          //ExAllocatePool ( PagedPool, (ULONG)newObject ); // initiate red-zone verification of object we allocated
+
+          if(W32Process != NULL)
+          {
+            InterlockedIncrement(&W32Process->GDIObjects);
+          }
+
+          DPRINT("GDIOBJ_AllocObj: 0x%x ob: 0x%x\n", Handle, ObjectBody);
+          return Handle;
+        }
+        else
+        {
+#ifdef GDI_DEBUG
+          if(++Attempts > 20)
+          {
+            DPRINT1("[%d]Waiting on 0x%x\n", Attempts, Handle);
+          }
+#endif
+          /* damn, someone is trying to lock the object even though it doesn't
+             eve nexist anymore, wait a little and try again!
+             FIXME - we shouldn't loop forever! Give up after some time! */
+          DelayExecution();
+          /* try again */
+          goto LockHandle;
+        }
+      }
+
+      ExFreeToPagedLookasideList(LookasideList, newObject);
+      DPRINT1("Failed to insert gdi object into the handle table, no handles left!\n");
+#ifdef DBG
+         if ( !leak_reported )
+         {
+                 DPRINT1("reporting gdi handle abusers:\n");
+                 int which;
+                 for ( which = 0; which < GDI_STACK_LEVELS; which++ )
+                     IntDumpHandleTable(which);
+                 leak_reported = 1;
+         }
+         else
+         {
+                 DPRINT1("gdi handle abusers already reported!\n");
+         }
+#endif//DBG
+    }
+    else
+    {
+      DPRINT1("Not enough memory to allocate gdi object!\n");
+    }
+  }
+  else
   {
-    W32Process->GDIObjects++;
+    DPRINT1("Failed to find lookaside list for object type 0x%x\n", ObjectType);
   }
-
-  return GDI_HANDLE_CREATE(Index, ObjectType);
+  return NULL;
 }
 
 /*!
@@ -384,77 +443,147 @@ GDIOBJ_AllocObj(WORD Size, DWORD ObjectType, GDICLEANUPPROC CleanupProc)
  * appropriate cleanup routine.
  *
  * \param hObj       - handle of the object to be deleted.
- * \param ObjectType - one of the \ref GDI object types
- * or GDI_OBJECT_TYPE_DONTCARE.
- * \param Flag       - if set to GDIOBJFLAG_IGNOREPID then the routine doesn't check if the process that
- * tries to delete the object is the same one that created it.
  *
  * \return Returns TRUE if succesful.
- *
- * \note You should only use GDIOBJFLAG_IGNOREPID if you are cleaning up after the process that terminated.
- * \note This function deferres object deletion if it is still in use.
+ * \return Returns FALSE if the cleanup routine returned FALSE or the object doesn't belong
+ * to the calling process.
 */
-BOOL STDCALL
-GDIOBJ_FreeObj(HGDIOBJ hObj, DWORD ObjectType, DWORD Flag)
+BOOL INTERNAL_CALL
+#ifdef GDI_DEBUG
+GDIOBJ_FreeObjDbg(const char* file, int line, HGDIOBJ hObj, DWORD ObjectType)
+#else /* !GDI_DEBUG */
+GDIOBJ_FreeObj(HGDIOBJ hObj, DWORD ObjectType)
+#endif /* GDI_DEBUG */
 {
-  PW32PROCESS W32Process;
-  PGDIOBJHDR objectHeader;
-  PGDIOBJ Obj;
+  PGDI_TABLE_ENTRY Entry;
   PPAGED_LOOKASIDE_LIST LookasideList;
-  BOOL         bRet = TRUE;
+  LONG ProcessId, LockedProcessId, PrevProcId, ExpectedType;
+#ifdef GDI_DEBUG
+  ULONG Attempts = 0;
+#endif
+
+  DPRINT("GDIOBJ_FreeObj: hObj: 0x%08x\n", hObj);
 
-  objectHeader = GDIOBJ_iGetObjectForIndex(GDI_HANDLE_GET_INDEX(hObj));
-  DPRINT("GDIOBJ_FreeObj: hObj: 0x%08x, object: %x\n", hObj, objectHeader);
+  if(GDI_HANDLE_IS_STOCKOBJ(hObj))
+  {
+    DPRINT1("GDIOBJ_FreeObj() failed, can't delete stock object handle: 0x%x !!!\n", hObj);
+#ifdef GDI_DEBUG
+    DPRINT1("-> called from %s:%i\n", file, line);
+#endif
+    return FALSE;
+  }
 
-  if (! GDI_VALID_OBJECT(hObj, objectHeader, ObjectType, Flag)
-      || GDI_GLOBAL_PROCESS == objectHeader->hProcessId)
+  /* shift the process id to the left so we can use the first bit to lock the object.
+     FIXME - don't shift once ROS' PIDs match with nt! */
+  ProcessId = (LONG)PsGetCurrentProcessId() << 1;
+  LockedProcessId = ProcessId | 0x1;
 
-    {
-      DPRINT1("Can't delete hObj:0x%08x, type:0x%08x, flag:%d\n", hObj, ObjectType, Flag);
-      return FALSE;
-    }
+  ExpectedType = ((ObjectType != GDI_OBJECT_TYPE_DONTCARE) ? ObjectType : 0);
 
-  DPRINT("FreeObj: locks: %x\n", objectHeader->dwCount );
-  if (!(Flag & GDIOBJFLAG_IGNORELOCK))
+  Entry = GDI_HANDLE_GET_ENTRY(HandleTable, hObj);
+
+LockHandle:
+  /* lock the object, we must not delete global objects, so don't exchange the locking
+     process ID to zero when attempting to lock a global object... */
+  PrevProcId = InterlockedCompareExchange(&Entry->ProcessId, LockedProcessId, ProcessId);
+  if(PrevProcId == ProcessId)
+  {
+    if(Entry->Type != 0 && Entry->KernelData != NULL && (ExpectedType == 0 || ((Entry->Type << 16) == ExpectedType)))
     {
-      /* check that the reference count is zero. if not then set flag
-       * and delete object when releaseobj is called */
-      ExAcquireFastMutex(&RefCountHandling);
-      if ((objectHeader->dwCount & ~0x80000000) > 0 )
-       {
-         DPRINT("GDIOBJ_FreeObj: delayed object deletion: count %d\n", objectHeader->dwCount);
-         objectHeader->dwCount |= 0x80000000;
-         ExReleaseFastMutex(&RefCountHandling);
-         return TRUE;
-       }
-      ExReleaseFastMutex(&RefCountHandling);
-    }
+      PGDIOBJHDR GdiHdr;
+
+      GdiHdr = GDIBdyToHdr(Entry->KernelData);
+
+      if(GdiHdr->LockingThread == NULL)
+      {
+        BOOL Ret;
+        PW32PROCESS W32Process = PsGetWin32Process();
+        ULONG Type = Entry->Type << 16;
+
+        /* Clear the type field so when unlocking the handle it gets finally deleted */
+        Entry->Type = 0;
+        Entry->KernelData = NULL;
 
-  /* allow object to delete internal data */
-  if (NULL != objectHeader->CleanupProc)
+        /* unlock the handle slot */
+        InterlockedExchange(&Entry->ProcessId, 0);
+
+        /* push this entry to the free list */
+        InterlockedPushEntrySList(&HandleTable->FreeEntriesHead,
+                                  &HandleTable->FreeEntries[GDI_ENTRY_TO_INDEX(HandleTable, Entry)]);
+
+        if(W32Process != NULL)
+        {
+          InterlockedDecrement(&W32Process->GDIObjects);
+        }
+
+        /* call the cleanup routine. */
+        Ret = RunCleanupCallback(GDIHdrToBdy(GdiHdr), Type);
+
+        /* Now it's time to free the memory */
+        LookasideList = FindLookasideList(Type);
+        if(LookasideList != NULL)
+        {
+          ExFreeToPagedLookasideList(LookasideList, GdiHdr);
+        }
+
+        return Ret;
+      }
+      else
+      {
+        /* the object is currently locked. just clear the type field so when the
+           object gets unlocked it will be finally deleted from the table. */
+        Entry->Type = 0;
+
+        /* unlock the handle slot */
+        InterlockedExchange(&Entry->ProcessId, 0);
+
+        /* report a successful deletion as the object is actually removed from the table */
+        return TRUE;
+      }
+    }
+    else
     {
-      Obj = (PGDIOBJ)((PCHAR)objectHeader + sizeof(GDIOBJHDR));
-      bRet = (*(objectHeader->CleanupProc))(Obj);
+      if(Entry->Type != 0)
+      {
+        DPRINT1("Attempted to delete object 0x%x, type mismatch (0x%x : 0x%x)\n", hObj, ObjectType, ExpectedType);
+      }
+      else
+      {
+        DPRINT1("Attempted to delete object 0x%x which was already deleted!\n", hObj);
+      }
+      InterlockedExchange(&Entry->ProcessId, PrevProcId);
     }
-  LookasideList = FindLookasideList(GDI_MAGIC_TO_TYPE(objectHeader->Magic));
-  if (NULL != LookasideList)
+  }
+  else if(PrevProcId == LockedProcessId)
+  {
+#ifdef GDI_DEBUG
+    if(++Attempts > 20)
     {
-      ExFreeToPagedLookasideList(LookasideList, objectHeader);
+      DPRINT1("[%d]Waiting on 0x%x\n", Attempts, hObj);
     }
-  ExAcquireFastMutexUnsafe (&HandleTableMutex);
-  HandleTable->Handles[GDI_HANDLE_GET_INDEX(hObj)] = NULL;
-#if GDI_COUNT_OBJECTS
-  HandleTable->HandlesCount--;
 #endif
-  ExReleaseFastMutexUnsafe (&HandleTableMutex);
-  
-  W32Process = PsGetCurrentProcess()->Win32Process;
-  if(W32Process)
+    /* the object is currently locked, wait some time and try again.
+       FIXME - we shouldn't loop forever! Give up after some time! */
+    DelayExecution();
+    /* try again */
+    goto LockHandle;
+  }
+  else
   {
-    W32Process->GDIObjects--;
+    if((PrevProcId >> 1) == 0)
+    {
+      DPRINT1("Attempted to free global gdi handle 0x%x, caller needs to get ownership first!!!", hObj);
+    }
+    else
+    {
+      DPRINT1("Attempted to free foreign handle: 0x%x Owner: 0x%x from Caller: 0x%x\n", hObj, PrevProcId >> 1, ProcessId >> 1);
+    }
+#ifdef GDI_DEBUG
+    DPRINT1("-> called from %s:%i\n", file, line);
+#endif
   }
 
-  return bRet;
+  return FALSE;
 }
 
 /*!
@@ -467,7 +596,7 @@ GDIOBJ_FreeObj(HGDIOBJ hObj, DWORD ObjectType, DWORD Flag)
  *
  * \note this function uses an O(n^2) algoritm because we shouldn't need to call it with more than 3 or 4 objects.
 */
-BOOL FASTCALL
+BOOL INTERNAL_CALL
 GDIOBJ_LockMultipleObj(PGDIMULTILOCK pList, INT nObj)
 {
   INT i, j;
@@ -509,7 +638,7 @@ GDIOBJ_LockMultipleObj(PGDIMULTILOCK pList, INT nObj)
  *
  * \note this function uses O(n^2) algoritm because we shouldn't need to call it with more than 3 or 4 objects.
 */
-BOOL FASTCALL
+BOOL INTERNAL_CALL
 GDIOBJ_UnlockMultipleObj(PGDIMULTILOCK pList, INT nObj)
 {
   INT i, j;
@@ -528,7 +657,7 @@ GDIOBJ_UnlockMultipleObj(PGDIMULTILOCK pList, INT nObj)
                  pList[j].pObj = NULL;
                }
            }
-         GDIOBJ_UnlockObj(pList[i].hObj, pList[i].ObjectType);
+         GDIOBJ_UnlockObj(pList[i].hObj);
          pList[i].pObj = NULL;
        }
     }
@@ -536,100 +665,16 @@ GDIOBJ_UnlockMultipleObj(PGDIMULTILOCK pList, INT nObj)
   return TRUE;
 }
 
-/*!
- * Get the type of the object.
- * \param      ObjectHandle - handle of the object.
- * \return     One of the \ref GDI object types
-*/
-DWORD FASTCALL
-GDIOBJ_GetObjectType(HGDIOBJ ObjectHandle)
-{
-  PGDIOBJHDR ObjHdr;
-
-  ObjHdr = GDIOBJ_iGetObjectForIndex(GDI_HANDLE_GET_INDEX(ObjectHandle));
-  if (NULL == ObjHdr
-      || ! GDI_VALID_OBJECT(ObjectHandle, ObjHdr, GDI_MAGIC_TO_TYPE(ObjHdr->Magic), 0))
-    {
-      DPRINT1("Invalid ObjectHandle 0x%08x\n", ObjectHandle);
-      return 0;
-    }
-  DPRINT("GDIOBJ_GetObjectType for handle 0x%08x returns 0x%08x\n", ObjectHandle,
-         GDI_MAGIC_TO_TYPE(ObjHdr->Magic));
-
-  return GDI_MAGIC_TO_TYPE(ObjHdr->Magic);
-}
-
 /*!
  * Initialization of the GDI object engine.
 */
-VOID FASTCALL
+VOID INTERNAL_CALL
 InitGdiObjectHandleTable (VOID)
 {
   DPRINT("InitGdiObjectHandleTable\n");
-  ExInitializeFastMutex (&HandleTableMutex);
-  ExInitializeFastMutex (&RefCountHandling);
-
-  HandleTable = GDIOBJ_iAllocHandleTable (GDI_HANDLE_COUNT);
-  DPRINT("HandleTable: %x\n", HandleTable );
-
-  InitEngHandleTable();
-}
-
-/*!
- * Creates a bunch of stock objects: brushes, pens, fonts.
-*/
-VOID FASTCALL
-CreateStockObjects(void)
-{
-  unsigned Object;
-
-  DPRINT("Beginning creation of stock objects\n");
-
-  /* Create GDI Stock Objects from the logical structures we've defined */
-
-  StockObjects[WHITE_BRUSH] =  IntGdiCreateBrushIndirect(&WhiteBrush);
-  StockObjects[LTGRAY_BRUSH] = IntGdiCreateBrushIndirect(&LtGrayBrush);
-  StockObjects[GRAY_BRUSH] =   IntGdiCreateBrushIndirect(&GrayBrush);
-  StockObjects[DKGRAY_BRUSH] = IntGdiCreateBrushIndirect(&DkGrayBrush);
-  StockObjects[BLACK_BRUSH] =  IntGdiCreateBrushIndirect(&BlackBrush);
-  StockObjects[NULL_BRUSH] =   IntGdiCreateBrushIndirect(&NullBrush);
-
-  StockObjects[WHITE_PEN] = IntGdiCreatePenIndirect(&WhitePen);
-  StockObjects[BLACK_PEN] = IntGdiCreatePenIndirect(&BlackPen);
-  StockObjects[NULL_PEN] =  IntGdiCreatePenIndirect(&NullPen);
-
-  (void) TextIntCreateFontIndirect(&OEMFixedFont, (HFONT*)&StockObjects[OEM_FIXED_FONT]);
-  (void) TextIntCreateFontIndirect(&AnsiFixedFont, (HFONT*)&StockObjects[ANSI_FIXED_FONT]);
-  (void) TextIntCreateFontIndirect(&SystemFont, (HFONT*)&StockObjects[SYSTEM_FONT]);
-  (void) TextIntCreateFontIndirect(&DeviceDefaultFont, (HFONT*)&StockObjects[DEVICE_DEFAULT_FONT]);
-  (void) TextIntCreateFontIndirect(&SystemFixedFont, (HFONT*)&StockObjects[SYSTEM_FIXED_FONT]);
-  (void) TextIntCreateFontIndirect(&DefaultGuiFont, (HFONT*)&StockObjects[DEFAULT_GUI_FONT]);
-
-  StockObjects[DEFAULT_PALETTE] = (HGDIOBJ*)PALETTE_Init();
-
-  for (Object = 0; Object < NB_STOCK_OBJECTS; Object++)
-    {
-      if (NULL != StockObjects[Object])
-       {
-         GDIOBJ_SetOwnership(StockObjects[Object], NULL);
-/*       GDI_HANDLE_SET_STOCKOBJ(StockObjects[Object]);*/
-       }
-    }
-
-  DPRINT("Completed creation of stock objects\n");
-}
 
-/*!
- * Return stock object.
- * \param      Object - stock object id.
- * \return     Handle to the object.
-*/
-HGDIOBJ STDCALL
-NtGdiGetStockObject(INT Object)
-{
-  DPRINT("NtGdiGetStockObject index %d\n", Object);
-
-  return ((Object < 0) || (NB_STOCK_OBJECTS <= Object)) ? NULL : StockObjects[Object];
+  HandleTable = GDIOBJ_iAllocHandleTable();
+  DPRINT("HandleTable: %x\n", HandleTable);
 }
 
 /*!
@@ -643,19 +688,21 @@ NtGdiDeleteObject(HGDIOBJ hObject)
   DPRINT("NtGdiDeleteObject handle 0x%08x\n", hObject);
 
   return NULL != hObject
-         ? GDIOBJ_FreeObj(hObject, GDI_OBJECT_TYPE_DONTCARE, GDIOBJFLAG_DEFAULT) : FALSE;
+         ? GDIOBJ_FreeObj(hObject, GDI_OBJECT_TYPE_DONTCARE) : FALSE;
 }
 
 /*!
  * Internal function. Called when the process is destroyed to free the remaining GDI handles.
  * \param      Process - PID of the process that will be destroyed.
 */
-BOOL FASTCALL
-CleanupForProcess (struct _EPROCESS *Process, INT Pid)
+BOOL INTERNAL_CALL
+GDI_CleanupForProcess (struct _EPROCESS *Process)
 {
-  DWORD i;
-  PGDIOBJHDR objectHeader;
+  PGDI_TABLE_ENTRY Entry, End;
   PEPROCESS CurrentProcess;
+  PW32PROCESS W32Process;
+  LONG ProcId;
+  ULONG Index = RESERVE_ENTRIES_COUNT;
 
   DPRINT("Starting CleanupForProcess prochandle %x Pid %d\n", Process, Pid);
   CurrentProcess = PsGetCurrentProcess();
@@ -663,19 +710,40 @@ CleanupForProcess (struct _EPROCESS *Process, INT Pid)
     {
       KeAttachProcess(Process);
     }
+  W32Process = Process->Win32Process;
+  ASSERT(W32Process);
 
-  for(i = 1; i < HandleTable->wTableSize; i++)
+  if(W32Process->GDIObjects > 0)
+  {
+    /* FIXME - Instead of building the handle here and delete it using GDIOBJ_FreeObj
+               we should delete it directly here! */
+    ProcId = ((LONG)Process->UniqueProcessId << 1);
+
+    End = &HandleTable->Entries[GDI_HANDLE_COUNT];
+    for(Entry = &HandleTable->Entries[RESERVE_ENTRIES_COUNT];
+        Entry < End;
+        Entry++, Index++)
     {
-      objectHeader = GDIOBJ_iGetObjectForIndex(i);
-      if (NULL != objectHeader &&
-          (INT) objectHeader->hProcessId == Pid)
-       {
-         DPRINT("CleanupForProcess: %d, process: %d, locks: %d, magic: 0x%x", i, objectHeader->hProcessId, objectHeader->dwCount, objectHeader->Magic);
-         GDIOBJ_FreeObj(GDI_HANDLE_CREATE(i, GDI_MAGIC_TO_TYPE(objectHeader->Magic)),
-                        GDI_MAGIC_TO_TYPE(objectHeader->Magic),
-                        GDIOBJFLAG_IGNOREPID | GDIOBJFLAG_IGNORELOCK);
-       }
+      /* ignore the lock bit */
+      if((Entry->ProcessId & ~0x1) == ProcId && Entry->Type != 0)
+      {
+        HGDIOBJ ObjectHandle;
+
+        /* Create the object handle for the entry, the upper 16 bit of the
+           Type field includes the type of the object including the stock
+           object flag - but since stock objects don't have a process id we can
+           simply ignore this fact here. */
+        ObjectHandle = (HGDIOBJ)(Index | (Entry->Type & 0xFFFF0000));
+
+        if(GDIOBJ_FreeObj(ObjectHandle, GDI_OBJECT_TYPE_DONTCARE) &&
+           W32Process->GDIObjects == 0)
+        {
+          /* there are no more gdi handles for this process, bail */
+          break;
+        }
+      }
     }
+  }
 
   if (CurrentProcess != Process)
     {
@@ -687,274 +755,732 @@ CleanupForProcess (struct _EPROCESS *Process, INT Pid)
   return TRUE;
 }
 
-#define GDIOBJ_TRACKLOCKS
-
-#ifdef GDIOBJ_LockObj
-#undef GDIOBJ_LockObj
-PGDIOBJ FASTCALL
+/*!
+ * Return pointer to the object by handle.
+ *
+ * \param hObj                 Object handle
+ * \return             Pointer to the object.
+ *
+ * \note Process can only get pointer to the objects it created or global objects.
+ *
+ * \todo Get rid of the ObjectType parameter!
+*/
+PGDIOBJ INTERNAL_CALL
+#ifdef GDI_DEBUG
 GDIOBJ_LockObjDbg (const char* file, int line, HGDIOBJ hObj, DWORD ObjectType)
+#else /* !GDI_DEBUG */
+GDIOBJ_LockObj (HGDIOBJ hObj, DWORD ObjectType)
+#endif /* GDI_DEBUG */
 {
-  PGDIOBJHDR ObjHdr = GDIOBJ_iGetObjectForIndex(GDI_HANDLE_GET_INDEX(hObj));
+  PGDI_TABLE_ENTRY Entry;
+  PETHREAD Thread;
+  LONG ProcessId, LockedProcessId, PrevProcId, ExpectedType;
+#ifdef GDI_DEBUG
+  ULONG Attempts = 0;
+#endif
 
-  DPRINT("(%s:%i) GDIOBJ_LockObjDbg(0x%08x,0x%08x)\n", file, line, hObj, ObjectType);
-  if (! GDI_VALID_OBJECT(hObj, ObjHdr, ObjectType, GDIOBJFLAG_DEFAULT))
-    {
-      int reason = 0;
-      if (NULL == ObjHdr)
-       {
-         reason = 1;
-       }
-      else if (GDI_MAGIC_TO_TYPE(ObjHdr->Magic) != ObjectType && ObjectType != GDI_OBJECT_TYPE_DONTCARE)
-       {
-         reason = 2;
-       }
-      else if (ObjHdr->hProcessId != GDI_GLOBAL_PROCESS
-          && ObjHdr->hProcessId != PsGetCurrentProcessId())
-       {
-         reason = 3;
-       }
-      else if (GDI_HANDLE_GET_TYPE(hObj) != ObjectType && ObjectType != GDI_OBJECT_TYPE_DONTCARE)
-       {
-         reason = 4;
-       }
-      DPRINT1("GDIOBJ_LockObj failed for 0x%08x, reqtype 0x%08x reason %d\n",
-              hObj, ObjectType, reason );
-      DPRINT1("\tcalled from: %s:%i\n", file, line );
-      return NULL;
-    }
+  DPRINT("GDIOBJ_LockObj: hObj: 0x%08x\n", hObj);
 
-#ifdef NDEBUG
-  ExAcquireFastMutex(&ObjHdr->Lock);
-#else /* NDEBUG */
-  if (! ExTryToAcquireFastMutex(&ObjHdr->Lock))
-    {
-      DPRINT1("Caution! GDIOBJ_LockObj trying to lock object 0x%x second time\n", hObj);
-      DPRINT1("  called from: %s:%i\n", file, line);
-      if (NULL != ObjHdr->lockfile)
-        {
-          DPRINT1("  previously locked from: %s:%i\n", ObjHdr->lockfile, ObjHdr->lockline);
-        }
-      ExAcquireFastMutex(&ObjHdr->Lock);
-      DPRINT1("  Disregard previous message about object 0x%x, it's ok\n", hObj);
-    }
-#endif /* NDEBUG */
+  Thread = PsGetCurrentThread();
 
-  ExAcquireFastMutex(&RefCountHandling);
-  ObjHdr->dwCount++;
-  ExReleaseFastMutex(&RefCountHandling);
+  /* shift the process id to the left so we can use the first bit to lock the object.
+     FIXME - don't shift once ROS' PIDs match with nt! */
+  ProcessId = (LONG)PsGetCurrentProcessId() << 1;
+  LockedProcessId = ProcessId | 0x1;
 
-  if (NULL == ObjHdr->lockfile)
-    {
-      ObjHdr->lockfile = file;
-      ObjHdr->lockline = line;
-    }
+  ExpectedType = ((ObjectType != GDI_OBJECT_TYPE_DONTCARE) ? ObjectType : 0);
 
-  return (PGDIOBJ)((PCHAR)ObjHdr + sizeof(GDIOBJHDR));
-}
-#endif//GDIOBJ_LockObj
+  Entry = GDI_HANDLE_GET_ENTRY(HandleTable, hObj);
 
-#ifdef GDIOBJ_UnlockObj
-#undef GDIOBJ_UnlockObj
-BOOL FASTCALL
-GDIOBJ_UnlockObjDbg (const char* file, int line, HGDIOBJ hObj, DWORD ObjectType)
-{
-  PGDIOBJHDR ObjHdr = GDIOBJ_iGetObjectForIndex(GDI_HANDLE_GET_INDEX(hObj));
+LockHandle:
+  /* lock the object, we must not delete stock objects, so don't check!!! */
+  PrevProcId = InterlockedCompareExchange(&Entry->ProcessId, LockedProcessId, ProcessId);
+  if(PrevProcId == ProcessId)
+  {
+    LONG EntryType = Entry->Type << 16;
 
-  if (! GDI_VALID_OBJECT(hObj, ObjHdr, ObjectType, GDIOBJFLAG_DEFAULT))
+    /* we're locking an object that belongs to our process or it's a global
+       object if ProcessId == 0 here. ProcessId can only be 0 here if it previously
+       failed to lock the object and it turned out to be a global object. */
+    if(EntryType != 0 && Entry->KernelData != NULL && (ExpectedType == 0 || (EntryType == ExpectedType)))
     {
-      DPRINT1("GDIBOJ_UnlockObj failed for 0x%08x, reqtype 0x%08x\n",
-                 hObj, ObjectType);
-      DPRINT1("\tcalled from: %s:%i\n", file, line);
-      return FALSE;
-    }
-  DPRINT("(%s:%i) GDIOBJ_UnlockObj(0x%08x,0x%08x)\n", file, line, hObj, ObjectType);
-  ObjHdr->lockfile = NULL;
-  ObjHdr->lockline = 0;
+      PETHREAD PrevThread;
+      PGDIOBJHDR GdiHdr;
 
-  return GDIOBJ_UnlockObj(hObj, ObjectType);
-}
-#endif//GDIOBJ_LockObj
+      GdiHdr = GDIBdyToHdr(Entry->KernelData);
 
-/*!
- * Return pointer to the object by handle.
- *
- * \param hObj                 Object handle
- * \param ObjectType   one of the object types defined in \ref GDI object types
- * \return             Pointer to the object.
- *
- * \note Process can only get pointer to the objects it created or global objects.
- *
- * \todo Don't allow to lock the objects twice! Synchronization!
-*/
-PGDIOBJ FASTCALL
-GDIOBJ_LockObj(HGDIOBJ hObj, DWORD ObjectType)
-{
-  PGDIOBJHDR ObjHdr = GDIOBJ_iGetObjectForIndex(GDI_HANDLE_GET_INDEX(hObj));
+      /* save the pointer to the calling thread so we know it was this thread
+         that locked the object. There's no need to do this atomically as we're
+         holding the lock of the handle slot, but this way it's easier ;) */
+      PrevThread = InterlockedCompareExchangePointer(&GdiHdr->LockingThread, Thread, NULL);
 
-  DPRINT("GDIOBJ_LockObj: hObj: 0x%08x, type: 0x%08x, objhdr: %x\n", hObj, ObjectType, ObjHdr);
-  if (! GDI_VALID_OBJECT(hObj, ObjHdr, ObjectType, GDIOBJFLAG_DEFAULT))
+      if(PrevThread == NULL || PrevThread == Thread)
+      {
+        if(++GdiHdr->Locks == 1)
+        {
+#ifdef GDI_DEBUG
+          GdiHdr->lockfile = file;
+          GdiHdr->lockline = line;
+#endif
+        }
+
+        InterlockedExchange(&Entry->ProcessId, PrevProcId);
+
+        /* we're done, return the object body */
+        return GDIHdrToBdy(GdiHdr);
+      }
+      else
+      {
+        InterlockedExchange(&Entry->ProcessId, PrevProcId);
+
+#ifdef GDI_DEBUG
+        if(++Attempts > 20)
+        {
+          DPRINT1("[%d]Waiting at %s:%i as 0x%x on 0x%x\n", Attempts, file, line, Thread, PrevThread);
+        }
+#endif
+
+        DelayExecution();
+        goto LockHandle;
+      }
+    }
+    else
     {
-      DPRINT1("GDIBOJ_LockObj failed for 0x%08x, type 0x%08x\n",
-                 hObj, ObjectType);
-      return NULL;
+      InterlockedExchange(&Entry->ProcessId, PrevProcId);
+
+      if(EntryType == 0)
+      {
+        DPRINT1("Attempted to lock object 0x%x that is deleted!\n", hObj);
+        KeRosDumpStackFrames ( NULL, 20 );
+      }
+      else
+      {
+        DPRINT1("Attempted to lock object 0x%x, type mismatch (0x%x : 0x%x)\n", hObj, EntryType, ExpectedType);
+        KeRosDumpStackFrames ( NULL, 20 );
+      }
+#ifdef GDI_DEBUG
+      DPRINT1("-> called from %s:%i\n", file, line);
+#endif
+    }
+  }
+  else if(PrevProcId == LockedProcessId)
+  {
+#ifdef GDI_DEBUG
+    if(++Attempts > 20)
+    {
+      DPRINT1("[%d]Waiting from %s:%i on 0x%x\n", Attempts, file, line, hObj);
     }
+#endif
+    /* the handle is currently locked, wait some time and try again.
+       FIXME - we shouldn't loop forever! Give up after some time! */
+    DelayExecution();
+    /* try again */
+    goto LockHandle;
+  }
+  else if((PrevProcId & ~0x1) == 0)
+  {
+    /* we're trying to lock a global object, change the ProcessId to 0 and try again */
+    ProcessId = 0x0;
+    LockedProcessId = ProcessId |0x1;
 
-  ExAcquireFastMutex(&ObjHdr->Lock);
+    goto LockHandle;
+  }
+  else
+  {
+    DPRINT1("Attempted to lock foreign handle: 0x%x, Owner: 0x%x locked: 0x%x Caller: 0x%x, stockobj: 0x%x\n", hObj, PrevProcId >> 1, PrevProcId & 0x1, PsGetCurrentProcessId(), GDI_HANDLE_IS_STOCKOBJ(hObj));
+    KeRosDumpStackFrames ( NULL, 20 );
+#ifdef GDI_DEBUG
+    DPRINT1("-> called from %s:%i\n", file, line);
+#endif
+  }
 
-  ExAcquireFastMutex(&RefCountHandling);
-  ObjHdr->dwCount++;
-  ExReleaseFastMutex(&RefCountHandling);
-  return (PGDIOBJ)((PCHAR)ObjHdr + sizeof(GDIOBJHDR));
+  return NULL;
 }
 
+
 /*!
  * Release GDI object. Every object locked by GDIOBJ_LockObj() must be unlocked. You should unlock the object
  * as soon as you don't need to have access to it's data.
 
  * \param hObj                 Object handle
- * \param ObjectType   one of the object types defined in \ref GDI object types
  *
  * \note This function performs delayed cleanup. If the object is locked when GDI_FreeObj() is called
  * then \em this function frees the object when reference count is zero.
- *
- * \todo Change synchronization algorithm.
 */
-#undef GDIOBJ_UnlockObj
-BOOL FASTCALL
-GDIOBJ_UnlockObj(HGDIOBJ hObj, DWORD ObjectType)
+BOOL INTERNAL_CALL
+#ifdef GDI_DEBUG
+GDIOBJ_UnlockObjDbg (const char* file, int line, HGDIOBJ hObj)
+#else /* !GDI_DEBUG */
+GDIOBJ_UnlockObj (HGDIOBJ hObj)
+#endif /* GDI_DEBUG */
 {
-  PGDIOBJHDR ObjHdr = GDIOBJ_iGetObjectForIndex(GDI_HANDLE_GET_INDEX(hObj));
+  PGDI_TABLE_ENTRY Entry;
+  PETHREAD Thread;
+  LONG ProcessId, LockedProcessId, PrevProcId;
+#ifdef GDI_DEBUG
+  ULONG Attempts = 0;
+#endif
 
-  DPRINT("GDIOBJ_UnlockObj: hObj: 0x%08x, type: 0x%08x, objhdr: %x\n", hObj, ObjectType, ObjHdr);
-  if (! GDI_VALID_OBJECT(hObj, ObjHdr, ObjectType, GDIOBJFLAG_DEFAULT))
-    {
-    DPRINT1( "GDIOBJ_UnLockObj: failed\n");
-    return FALSE;
-  }
+  DPRINT("GDIOBJ_UnlockObj: hObj: 0x%08x\n", hObj);
+  Thread = PsGetCurrentThread();
 
-  ExReleaseFastMutex(&ObjHdr->Lock);
+  /* shift the process id to the left so we can use the first bit to lock the object.
+     FIXME - don't shift once ROS' PIDs match with nt! */
+  ProcessId = (LONG)PsGetCurrentProcessId() << 1;
+  LockedProcessId = ProcessId | 0x1;
 
-  ExAcquireFastMutex(&RefCountHandling);
-  if (0 == (ObjHdr->dwCount & ~0x80000000))
+  Entry = GDI_HANDLE_GET_ENTRY(HandleTable, hObj);
+
+LockHandle:
+  /* lock the handle, we must not delete stock objects, so don't check!!! */
+  PrevProcId = InterlockedCompareExchange(&Entry->ProcessId, LockedProcessId, ProcessId);
+  if(PrevProcId == ProcessId)
+  {
+    /* we're unlocking an object that belongs to our process or it's a global
+       object if ProcessId == 0 here. ProcessId can only be 0 here if it previously
+       failed to lock the object and it turned out to be a global object. */
+    if(Entry->KernelData != NULL)
     {
-      ExReleaseFastMutex(&RefCountHandling);
-      DPRINT1( "GDIOBJ_UnLockObj: unlock object (0x%x) that is not locked\n", hObj );
-      return FALSE;
-    }
+      PETHREAD PrevThread;
+      PGDIOBJHDR GdiHdr;
+
+      GdiHdr = GDIBdyToHdr(Entry->KernelData);
+         //ExAllocatePool ( PagedPool, (ULONG)GdiHdr ); // initiate red-zone validation on this block
+
+      PrevThread = GdiHdr->LockingThread;
+      if(PrevThread == Thread)
+      {
+        BOOL Ret;
+
+        if(--GdiHdr->Locks == 0)
+        {
+          GdiHdr->LockingThread = NULL;
+
+#ifdef GDI_DEBUG
+          GdiHdr->lockfile = NULL;
+          GdiHdr->lockline = 0;
+#endif
+        }
+
+        if(Entry->Type == 0 && GdiHdr->Locks == 0)
+        {
+          PPAGED_LOOKASIDE_LIST LookasideList;
+          PW32PROCESS W32Process = PsGetWin32Process();
+          DWORD Type = GDI_HANDLE_GET_TYPE(hObj);
 
-  ObjHdr->dwCount--;
+          ASSERT(ProcessId != 0); /* must not delete a global handle!!!! */
 
-  if (ObjHdr->dwCount == 0x80000000)
+          /* we should delete the handle */
+          Entry->KernelData = NULL;
+          InterlockedExchange(&Entry->ProcessId, 0);
+
+          InterlockedPushEntrySList(&HandleTable->FreeEntriesHead,
+                                    &HandleTable->FreeEntries[GDI_ENTRY_TO_INDEX(HandleTable, Entry)]);
+
+          if(W32Process != NULL)
+          {
+            InterlockedDecrement(&W32Process->GDIObjects);
+          }
+
+          /* call the cleanup routine. */
+          Ret = RunCleanupCallback(GDIHdrToBdy(GdiHdr), Type);
+
+          /* Now it's time to free the memory */
+          LookasideList = FindLookasideList(Type);
+          if(LookasideList != NULL)
+          {
+            ExFreeToPagedLookasideList(LookasideList, GdiHdr);
+          }
+        }
+        else
+        {
+          /* remove the handle slot lock */
+          InterlockedExchange(&Entry->ProcessId, PrevProcId);
+          Ret = TRUE;
+        }
+
+        /* we're done*/
+        return Ret;
+      }
+#ifdef GDI_DEBUG
+      else if(PrevThread != NULL)
+      {
+        DPRINT1("Attempted to unlock object 0x%x, previously locked by other thread (0x%x) from %s:%i (called from %s:%i)\n",
+                hObj, PrevThread, GdiHdr->lockfile, GdiHdr->lockline, file, line);
+        InterlockedExchange(&Entry->ProcessId, PrevProcId);
+      }
+#endif
+      else
+      {
+#ifdef GDI_DEBUG
+        if(++Attempts > 20)
+        {
+          DPRINT1("[%d]Waiting at %s:%i as 0x%x on 0x%x\n", Attempts, file, line, Thread, PrevThread);
+        }
+#endif
+        /* FIXME - we should give up after some time unless we want to wait forever! */
+        InterlockedExchange(&Entry->ProcessId, PrevProcId);
+
+        DelayExecution();
+        goto LockHandle;
+      }
+    }
+    else
     {
-      //delayed object release
-      ObjHdr->dwCount = 0;
-      ExReleaseFastMutex(&RefCountHandling);
-      DPRINT("GDIOBJ_UnlockObj: delayed delete\n");
-      return GDIOBJ_FreeObj(hObj, ObjectType, GDIOBJFLAG_DEFAULT);
+      InterlockedExchange(&Entry->ProcessId, PrevProcId);
+      DPRINT1("Attempted to unlock object 0x%x that is deleted!\n", hObj);
     }
-  ExReleaseFastMutex(&RefCountHandling);
+  }
+  else if(PrevProcId == LockedProcessId)
+  {
+#ifdef GDI_DEBUG
+    if(++Attempts > 20)
+    {
+      DPRINT1("[%d]Waiting from %s:%i on 0x%x\n", Attempts, file, line, hObj);
+    }
+#endif
+    /* the handle is currently locked, wait some time and try again.
+       FIXME - we shouldn't loop forever! Give up after some time! */
+    DelayExecution();
+    /* try again */
+    goto LockHandle;
+  }
+  else if((PrevProcId & ~0x1) == 0)
+  {
+    /* we're trying to unlock a global object, change the ProcessId to 0 and try again */
+    ProcessId = 0x0;
+    LockedProcessId = ProcessId |0x1;
 
-  return TRUE;
+    goto LockHandle;
+  }
+  else
+  {
+    DPRINT1("Attempted to unlock foreign handle: 0x%x, Owner: 0x%x locked: 0x%x Caller: 0x%x, stockobj: 0x%x\n", hObj, PrevProcId >> 1, PrevProcId & 0x1, PsGetCurrentProcessId(), GDI_HANDLE_IS_STOCKOBJ(hObj));
+  }
+
+  return FALSE;
 }
 
-BOOL FASTCALL
+BOOL INTERNAL_CALL
 GDIOBJ_OwnedByCurrentProcess(HGDIOBJ ObjectHandle)
 {
-  PGDIOBJHDR ObjHdr = GDIOBJ_iGetObjectForIndex(GDI_HANDLE_GET_INDEX(ObjectHandle));
+  PGDI_TABLE_ENTRY Entry;
+  LONG ProcessId;
+  BOOL Ret;
 
   DPRINT("GDIOBJ_OwnedByCurrentProcess: ObjectHandle: 0x%08x\n", ObjectHandle);
-  ASSERT(GDI_VALID_OBJECT(ObjectHandle, ObjHdr, GDI_OBJECT_TYPE_DONTCARE, GDIOBJFLAG_IGNOREPID));
 
-  return ObjHdr->hProcessId == PsGetCurrentProcessId();
+  if(!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle))
+  {
+    ProcessId = (LONG)PsGetCurrentProcessId() << 1;
+
+    Entry = GDI_HANDLE_GET_ENTRY(HandleTable, ObjectHandle);
+    Ret = Entry->KernelData != NULL &&
+          Entry->Type != 0 &&
+          (Entry->ProcessId & ~0x1) == ProcessId;
+
+    return Ret;
+  }
+
+  return FALSE;
 }
 
-void FASTCALL
-GDIOBJ_SetOwnership(HGDIOBJ ObjectHandle, PEPROCESS NewOwner)
+BOOL INTERNAL_CALL
+GDIOBJ_ConvertToStockObj(HGDIOBJ *hObj)
 {
-  PGDIOBJHDR ObjHdr = GDIOBJ_iGetObjectForIndex(GDI_HANDLE_GET_INDEX(ObjectHandle));
-  PEPROCESS OldProcess;
-  PW32PROCESS W32Process;
-  NTSTATUS Status;
+/*
+ * FIXME !!!!! THIS FUNCTION NEEDS TO BE FIXED - IT IS NOT SAFE WHEN OTHER THREADS
+ *             MIGHT ATTEMPT TO LOCK THE OBJECT DURING THIS CALL!!!
+ */
+  PGDI_TABLE_ENTRY Entry;
+  LONG ProcessId, LockedProcessId, PrevProcId;
+  PETHREAD Thread;
+#ifdef GDI_DEBUG
+  ULONG Attempts = 0;
+#endif
 
-  DPRINT("GDIOBJ_OwnedByCurrentProcess: ObjectHandle: 0x%08x\n", ObjectHandle);
-  ASSERT(GDI_VALID_OBJECT(ObjectHandle, ObjHdr, GDI_OBJECT_TYPE_DONTCARE, GDIOBJFLAG_IGNOREPID));
+  ASSERT(hObj);
+
+  DPRINT("GDIOBJ_ConvertToStockObj: hObj: 0x%08x\n", *hObj);
 
-  if ((NULL == NewOwner && GDI_GLOBAL_PROCESS != ObjHdr->hProcessId)
-      || (NULL != NewOwner && ObjHdr->hProcessId != (HANDLE) NewOwner->UniqueProcessId))
+  Thread = PsGetCurrentThread();
+
+  if(!GDI_HANDLE_IS_STOCKOBJ(*hObj))
+  {
+    /* shift the process id to the left so we can use the first bit to lock the object.
+       FIXME - don't shift once ROS' PIDs match with nt! */
+    ProcessId = (LONG)PsGetCurrentProcessId() << 1;
+    LockedProcessId = ProcessId | 0x1;
+
+    Entry = GDI_HANDLE_GET_ENTRY(HandleTable, *hObj);
+
+LockHandle:
+    /* lock the object, we must not convert stock objects, so don't check!!! */
+    PrevProcId = InterlockedCompareExchange(&Entry->ProcessId, LockedProcessId, ProcessId);
+    if(PrevProcId == ProcessId)
     {
-      Status = PsLookupProcessByProcessId((PVOID)ObjHdr->hProcessId, &OldProcess);
-      if (NT_SUCCESS(Status))
+      LONG NewType, PrevType, OldType;
+
+      /* we're locking an object that belongs to our process. First calculate
+         the new object type including the stock object flag and then try to
+         exchange it.*/
+      NewType = GDI_HANDLE_GET_TYPE(*hObj);
+      NewType |= NewType >> 16;
+      /* This is the type that the object should have right now, save it */
+      OldType = NewType;
+      /* As the object should be a stock object, set it's flag, but only in the upper 16 bits */
+      NewType |= GDI_HANDLE_STOCK_MASK;
+
+      /* Try to exchange the type field - but only if the old (previous type) matches! */
+      PrevType = InterlockedCompareExchange(&Entry->Type, NewType, OldType);
+      if(PrevType == OldType && Entry->KernelData != NULL)
+      {
+        PETHREAD PrevThread;
+        PGDIOBJHDR GdiHdr;
+
+        /* We successfully set the stock object flag.
+           KernelData should never be NULL here!!! */
+        ASSERT(Entry->KernelData);
+
+        GdiHdr = GDIBdyToHdr(Entry->KernelData);
+
+        PrevThread = GdiHdr->LockingThread;
+        if(PrevThread == NULL || PrevThread == Thread)
         {
-          W32Process = OldProcess->Win32Process;
-          if (W32Process)
+          /* dereference the process' object counter */
+          if(PrevProcId != GDI_GLOBAL_PROCESS)
+          {
+            PEPROCESS OldProcess;
+            PW32PROCESS W32Process;
+            NTSTATUS Status;
+
+            /* FIXME */
+            Status = PsLookupProcessByProcessId((PVOID)(PrevProcId >> 1), &OldProcess);
+            if(NT_SUCCESS(Status))
             {
-              W32Process->GDIObjects--;
+              W32Process = OldProcess->Win32Process;
+              if(W32Process != NULL)
+              {
+                InterlockedDecrement(&W32Process->GDIObjects);
+              }
+              ObDereferenceObject(OldProcess);
             }
-          ObDereferenceObject(OldProcess);
+          }
+
+          /* remove the process id lock and make it global */
+          InterlockedExchange(&Entry->ProcessId, GDI_GLOBAL_PROCESS);
+
+          *hObj = (HGDIOBJ)((ULONG)(*hObj) | GDI_HANDLE_STOCK_MASK);
+
+          /* we're done, successfully converted the object */
+          return TRUE;
         }
-    }
+        else
+        {
+#ifdef GDI_DEBUG
+          if(++Attempts > 20)
+          {
+            if(GdiHdr->lockfile != NULL)
+            {
+              DPRINT1("[%d]Locked %s:%i by 0x%x (we're 0x%x)\n", Attempts, GdiHdr->lockfile, GdiHdr->lockline, PrevThread, Thread);
+            }
+          }
+#endif
+          /* WTF?! The object is already locked by a different thread!
+             Release the lock, wait a bit and try again!
+             FIXME - we should give up after some time unless we want to wait forever! */
+          InterlockedExchange(&Entry->ProcessId, PrevProcId);
 
-  if (NULL == NewOwner)
+          DelayExecution();
+          goto LockHandle;
+        }
+      }
+      else
+      {
+        DPRINT1("Attempted to convert object 0x%x that is deleted! Should never get here!!!\n", hObj);
+      }
+    }
+    else if(PrevProcId == LockedProcessId)
     {
-      ObjHdr->hProcessId = GDI_GLOBAL_PROCESS;
+#ifdef GDI_DEBUG
+    if(++Attempts > 20)
+    {
+      DPRINT1("[%d]Waiting on 0x%x\n", Attempts, hObj);
+    }
+#endif
+      /* the object is currently locked, wait some time and try again.
+         FIXME - we shouldn't loop forever! Give up after some time! */
+      DelayExecution();
+      /* try again */
+      goto LockHandle;
     }
-  else if (ObjHdr->hProcessId != (HANDLE) NewOwner->UniqueProcessId)
+    else
     {
-      ObjHdr->hProcessId = (HANDLE) NewOwner->UniqueProcessId;
-      W32Process = NewOwner->Win32Process;
-      if (W32Process)
-        {
-          W32Process->GDIObjects++;
-        }
+      DPRINT1("Attempted to convert invalid handle: 0x%x\n", hObj);
     }
+  }
+
+  return FALSE;
 }
 
-void FASTCALL
-GDIOBJ_CopyOwnership(HGDIOBJ CopyFrom, HGDIOBJ CopyTo)
+void INTERNAL_CALL
+GDIOBJ_SetOwnership(HGDIOBJ ObjectHandle, PEPROCESS NewOwner)
 {
-  PGDIOBJHDR ObjHdrFrom = GDIOBJ_iGetObjectForIndex(GDI_HANDLE_GET_INDEX(CopyFrom));
-  PGDIOBJHDR ObjHdrTo = GDIOBJ_iGetObjectForIndex(GDI_HANDLE_GET_INDEX(CopyTo));
-  NTSTATUS Status;
-  PEPROCESS ProcessFrom;
-  PEPROCESS CurrentProcess;
+  PGDI_TABLE_ENTRY Entry;
+  LONG ProcessId, LockedProcessId, PrevProcId;
+  PETHREAD Thread;
+#ifdef GDI_DEBUG
+  ULONG Attempts = 0;
+#endif
+
+  DPRINT("GDIOBJ_SetOwnership: hObj: 0x%x, NewProcess: 0x%x\n", ObjectHandle, (NewOwner ? PsGetProcessId(NewOwner) : 0));
+
+  Thread = PsGetCurrentThread();
+
+  if(!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle))
+  {
+    /* shift the process id to the left so we can use the first bit to lock the object.
+       FIXME - don't shift once ROS' PIDs match with nt! */
+    ProcessId = (LONG)PsGetCurrentProcessId() << 1;
+    LockedProcessId = ProcessId | 0x1;
 
-  ASSERT(NULL != ObjHdrFrom && NULL != ObjHdrTo);
-  if (NULL != ObjHdrFrom && NULL != ObjHdrTo
-      && ObjHdrTo->hProcessId != ObjHdrFrom->hProcessId)
+    Entry = GDI_HANDLE_GET_ENTRY(HandleTable, ObjectHandle);
+
+LockHandle:
+    /* lock the object, we must not convert stock objects, so don't check!!! */
+    PrevProcId = InterlockedCompareExchange(&Entry->ProcessId, ProcessId, LockedProcessId);
+    if(PrevProcId == ProcessId)
     {
-      if (ObjHdrFrom->hProcessId == GDI_GLOBAL_PROCESS)
+      PETHREAD PrevThread;
+
+      if(Entry->Type != 0 && Entry->KernelData != NULL)
+      {
+        PGDIOBJHDR GdiHdr = GDIBdyToHdr(Entry->KernelData);
+
+        PrevThread = GdiHdr->LockingThread;
+        if(PrevThread == NULL || PrevThread == Thread)
         {
-          GDIOBJ_SetOwnership(CopyTo, NULL);
+          PEPROCESS OldProcess;
+          PW32PROCESS W32Process;
+          NTSTATUS Status;
+
+          /* dereference the process' object counter */
+          /* FIXME */
+          Status = PsLookupProcessByProcessId((PVOID)(PrevProcId >> 1), &OldProcess);
+          if(NT_SUCCESS(Status))
+          {
+            W32Process = OldProcess->Win32Process;
+            if(W32Process != NULL)
+            {
+              InterlockedDecrement(&W32Process->GDIObjects);
+            }
+            ObDereferenceObject(OldProcess);
+          }
+
+          if(NewOwner != NULL)
+          {
+            /* FIXME */
+            ProcessId = (LONG)PsGetProcessId(NewOwner) << 1;
+
+            /* Increase the new process' object counter */
+            W32Process = NewOwner->Win32Process;
+            if(W32Process != NULL)
+            {
+              InterlockedIncrement(&W32Process->GDIObjects);
+            }
+          }
+          else
+            ProcessId = 0;
+
+          /* remove the process id lock and change it to the new process id */
+          InterlockedExchange(&Entry->ProcessId, ProcessId);
+
+          /* we're done! */
+          return;
         }
+        else
+        {
+#ifdef GDI_DEBUG
+          if(++Attempts > 20)
+          {
+            if(GdiHdr->lockfile != NULL)
+            {
+              DPRINT1("[%d]Locked from %s:%i by 0x%x (we're 0x%x)\n", Attempts, GdiHdr->lockfile, GdiHdr->lockline, PrevThread, Thread);
+            }
+          }
+#endif
+          /* WTF?! The object is already locked by a different thread!
+             Release the lock, wait a bit and try again! DO reset the pid lock
+             so we make sure we don't access invalid memory in case the object is
+             being deleted in the meantime (because we don't have aquired a reference
+             at this point).
+             FIXME - we should give up after some time unless we want to wait forever! */
+          InterlockedExchange(&Entry->ProcessId, PrevProcId);
+
+          DelayExecution();
+          goto LockHandle;
+        }
+      }
       else
+      {
+        DPRINT1("Attempted to change ownership of an object 0x%x currently being destroyed!!!\n", ObjectHandle);
+      }
+    }
+    else if(PrevProcId == LockedProcessId)
+    {
+#ifdef GDI_DEBUG
+    if(++Attempts > 20)
+    {
+      DPRINT1("[%d]Waiting on 0x%x\n", Attempts, ObjectHandle);
+    }
+#endif
+      /* the object is currently locked, wait some time and try again.
+         FIXME - we shouldn't loop forever! Give up after some time! */
+      DelayExecution();
+      /* try again */
+      goto LockHandle;
+    }
+    else if((PrevProcId >> 1) == 0)
+    {
+      /* allow changing ownership of global objects */
+      ProcessId = 0;
+      LockedProcessId = ProcessId | 0x1;
+      goto LockHandle;
+    }
+    else if((PrevProcId >> 1) != (LONG)PsGetCurrentProcessId())
+    {
+      DPRINT1("Attempted to change ownership of object 0x%x (pid: 0x%x) from pid 0x%x!!!\n", ObjectHandle, PrevProcId >> 1, PsGetCurrentProcessId());
+    }
+    else
+    {
+      DPRINT1("Attempted to change owner of invalid handle: 0x%x\n", ObjectHandle);
+    }
+  }
+}
+
+void INTERNAL_CALL
+GDIOBJ_CopyOwnership(HGDIOBJ CopyFrom, HGDIOBJ CopyTo)
+{
+  PGDI_TABLE_ENTRY FromEntry;
+  PETHREAD Thread;
+  LONG FromProcessId, FromLockedProcessId, FromPrevProcId;
+#ifdef GDI_DEBUG
+  ULONG Attempts = 0;
+#endif
+
+  DPRINT("GDIOBJ_CopyOwnership: from: 0x%x, to: 0x%x\n", CopyFrom, CopyTo);
+
+  Thread = PsGetCurrentThread();
+
+  if(!GDI_HANDLE_IS_STOCKOBJ(CopyFrom) && !GDI_HANDLE_IS_STOCKOBJ(CopyTo))
+  {
+    FromEntry = GDI_HANDLE_GET_ENTRY(HandleTable, CopyFrom);
+
+    FromProcessId = FromEntry->ProcessId & ~0x1;
+    FromLockedProcessId = FromProcessId | 0x1;
+
+LockHandleFrom:
+    /* lock the object, we must not convert stock objects, so don't check!!! */
+    FromPrevProcId = InterlockedCompareExchange(&FromEntry->ProcessId, FromProcessId, FromLockedProcessId);
+    if(FromPrevProcId == FromProcessId)
+    {
+      PETHREAD PrevThread;
+      PGDIOBJHDR GdiHdr;
+
+      if(FromEntry->Type != 0 && FromEntry->KernelData != NULL)
+      {
+        GdiHdr = GDIBdyToHdr(FromEntry->KernelData);
+
+        /* save the pointer to the calling thread so we know it was this thread
+           that locked the object */
+        PrevThread = GdiHdr->LockingThread;
+        if(PrevThread == NULL || PrevThread == Thread)
         {
-          /* Warning: ugly hack ahead
-           *
-           * During process cleanup, we can't call PsLookupProcessByProcessId
-           * for the current process, 'cause that function will try to
-           * reference the process, and since the process is closing down
-           * that will result in a bugcheck.
-           * So, instead, we call PsGetCurrentProcess, which doesn't reference
-           * the process. If the current process is indeed the one we're
-           * looking for, we use it, otherwise we can (safely) call
-           * PsLookupProcessByProcessId
-           */
-          CurrentProcess = PsGetCurrentProcess();
-          if (ObjHdrFrom->hProcessId == (HANDLE) CurrentProcess->UniqueProcessId)
+          /* now let's change the ownership of the target object */
+
+          if((FromPrevProcId & ~0x1) != 0)
+          {
+            PEPROCESS ProcessTo;
+            /* FIXME */
+            if(NT_SUCCESS(PsLookupProcessByProcessId((PVOID)(FromPrevProcId >> 1), &ProcessTo)))
             {
-              GDIOBJ_SetOwnership(CopyTo, CurrentProcess);
+              GDIOBJ_SetOwnership(CopyTo, ProcessTo);
+              ObDereferenceObject(ProcessTo);
             }
+          }
           else
+          {
+            /* mark the object as global */
+            GDIOBJ_SetOwnership(CopyTo, NULL);
+          }
+
+          InterlockedExchange(&FromEntry->ProcessId, FromPrevProcId);
+        }
+        else
+        {
+#ifdef GDI_DEBUG
+          if(++Attempts > 20)
+          {
+            if(GdiHdr->lockfile != NULL)
             {
-              Status = PsLookupProcessByProcessId((PVOID) ObjHdrFrom->hProcessId, &ProcessFrom);
-              if (NT_SUCCESS(Status))
-                {
-                  GDIOBJ_SetOwnership(CopyTo, ProcessFrom);
-                  ObDereferenceObject(ProcessFrom);
-                }
+              DPRINT1("[%d]Locked from %s:%i by 0x%x (we're 0x%x)\n", Attempts, GdiHdr->lockfile, GdiHdr->lockline, PrevThread, Thread);
             }
+          }
+#endif
+          /* WTF?! The object is already locked by a different thread!
+             Release the lock, wait a bit and try again! DO reset the pid lock
+             so we make sure we don't access invalid memory in case the object is
+             being deleted in the meantime (because we don't have aquired a reference
+             at this point).
+             FIXME - we should give up after some time unless we want to wait forever! */
+          InterlockedExchange(&FromEntry->ProcessId, FromPrevProcId);
+
+          DelayExecution();
+          goto LockHandleFrom;
         }
+      }
+      else
+      {
+        DPRINT1("Attempted to copy ownership from an object 0x%x currently being destroyed!!!\n", CopyFrom);
+      }
     }
+    else if(FromPrevProcId == FromLockedProcessId)
+    {
+#ifdef GDI_DEBUG
+    if(++Attempts > 20)
+    {
+      DPRINT1("[%d]Waiting on 0x%x\n", Attempts, CopyFrom);
+    }
+#endif
+      /* the object is currently locked, wait some time and try again.
+         FIXME - we shouldn't loop forever! Give up after some time! */
+      DelayExecution();
+      /* try again */
+      goto LockHandleFrom;
+    }
+    else if((FromPrevProcId >> 1) != (LONG)PsGetCurrentProcessId())
+    {
+      /* FIXME - should we really allow copying ownership from objects that we don't even own? */
+      DPRINT1("WARNING! Changing copying ownership of object 0x%x (pid: 0x%x) to pid 0x%x!!!\n", CopyFrom, FromPrevProcId >> 1, PsGetCurrentProcessId());
+      FromProcessId = FromPrevProcId & ~0x1;
+      FromLockedProcessId = FromProcessId | 0x1;
+      goto LockHandleFrom;
+    }
+    else
+    {
+      DPRINT1("Attempted to copy ownership from invalid handle: 0x%x\n", CopyFrom);
+    }
+  }
+}
+
+PVOID INTERNAL_CALL
+GDI_MapHandleTable(HANDLE hProcess)
+{
+  DPRINT("%s:%i: %s(): FIXME - Map handle table into the process memory space!\n",
+         __FILE__, __LINE__, __FUNCTION__);
+  /* FIXME - Map the entire gdi handle table read-only to userland into the
+             scope of hProcess and return the pointer */
+  return NULL;
 }
 
 /* EOF */