HACK: hard-coded gdi handle quotas, excepting code paths I haven't found yet, reactos...
[reactos.git] / reactos / subsys / win32k / objects / gdiobj.c
index 4a4a0f2..7c5d364 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.51 2003/11/25 22:06:31 gvg Exp $
- *
+ * $Id: gdiobj.c,v 1.80 2004/12/19 00:03:56 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>
 #define NDEBUG
-#include <win32k/debug1.h>
+#include <debug.h>
 
-/*! 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
+#ifdef __USE_W32API
+/* F*(&#$ header mess!!!! */
+HANDLE
+STDCALL PsGetProcessId(
+       PEPROCESS       Process
+       );
+#endif /* __USE_W32API */
 
-typedef struct _GDI_HANDLE_TABLE
-{
-  WORD  wTableSize;
-  PGDIOBJHDR Handles[1];
-} GDI_HANDLE_TABLE, *PGDI_HANDLE_TABLE;
 
-/*  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 };
+#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))])
 
-static LOGBRUSH GrayBrush =
-/* FIXME : this should perhaps be BS_HATCHED, at least for 1 bitperpixel */
-{ BS_SOLID, RGB(128,128,128), 0 };
+#define GDIBdyToHdr(body)                                                      \
+  ((PGDIOBJHDR)(body) - 1)
+#define GDIHdrToBdy(hdr)                                                       \
+  (PGDIOBJ)((PGDIOBJHDR)(hdr) + 1)
 
-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 };
+/* apparently the first 10 entries are never used in windows as they are empty */
+#define RESERVE_ENTRIES_COUNT 10
 
-static LOGBRUSH BlackBrush =
-{ BS_SOLID, RGB(0,0,0), 0 };
+typedef struct _GDI_HANDLE_TABLE
+{
+  PPAGED_LOOKASIDE_LIST LookasideLists;
 
-static LOGBRUSH NullBrush =
-{ BS_NULL, 0, 0 };
+  SLIST_HEADER FreeEntriesHead;
+  SLIST_ENTRY FreeEntries[((GDI_HANDLE_COUNT * sizeof(GDI_TABLE_ENTRY)) << 3) /
+                          (sizeof(SLIST_ENTRY) << 3)];
 
-static LOGPEN WhitePen =
-{ PS_SOLID, { 0, 0 }, RGB(255,255,255) };
+  GDI_TABLE_ENTRY Entries[GDI_HANDLE_COUNT];
+} GDI_HANDLE_TABLE, *PGDI_HANDLE_TABLE;
 
-static LOGPEN BlackPen =
-{ PS_SOLID, { 0, 0 }, RGB(0,0,0) };
+typedef struct
+{
+  ULONG Type;
+  ULONG Size;
+  GDICLEANUPPROC CleanupProc;
+} GDI_OBJ_INFO, *PGDI_OBJ_INFO;
 
-static LOGPEN NullPen =
-{ PS_NULL, { 0, 0 }, 0 };
+/*
+ * Dummy GDI Cleanup Callback
+ */
+BOOL INTERNAL_CALL
+GDI_CleanupDummy(PVOID ObjectBody)
+{
+  return TRUE;
+}
 
-static LOGFONTW OEMFixedFont =
-{ 14, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, OEM_CHARSET,
-  0, 0, DEFAULT_QUALITY, FIXED_PITCH | FF_MODERN, L"" };
+/* Testing shows that regions are the most used GDIObj type,
+   so put that one first for performance */
+const
+GDI_OBJ_INFO ObjInfo[] =
+{
+   /* 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(ObjInfo) / sizeof(ObjInfo[0]))
+
+static PGDI_HANDLE_TABLE HandleTable = NULL;
+static LARGE_INTEGER ShortDelay;
+
+#define DelayExecution() \
+  DPRINT("%s:%i: Delay\n", __FILE__, __LINE__); \
+  KeDelayExecutionThread(KernelMode, FALSE, &ShortDelay)
 
-static LOGFONTW AnsiFixedFont =
-{ 14, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET,
-  0, 0, DEFAULT_QUALITY, FIXED_PITCH | FF_MODERN, L"" };
+/*!
+ * Allocate GDI object table.
+ * \param      Size - number of entries in the object table.
+*/
+static PGDI_HANDLE_TABLE INTERNAL_CALL
+GDIOBJ_iAllocHandleTable(VOID)
+{
+  PGDI_HANDLE_TABLE handleTable;
+  UINT ObjType;
+  UINT i;
+  PGDI_TABLE_ENTRY Entry;
 
-/*static LOGFONTW AnsiVarFont =
- *{ 14, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET,
- *  0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS, L"MS Sans Serif" }; */
+  handleTable = ExAllocatePoolWithTag(NonPagedPool, sizeof(GDI_HANDLE_TABLE), TAG_GDIHNDTBLE);
+  ASSERT( handleTable );
+  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]);
+  }
 
-static LOGFONTW SystemFont =
-{ 14, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET,
-  0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS, L"System" };
+  handleTable->LookasideLists = ExAllocatePoolWithTag(NonPagedPool,
+                                                      OBJTYPE_COUNT * sizeof(PAGED_LOOKASIDE_LIST),
+                                                      TAG_GDIHNDTBLE);
+  if(handleTable->LookasideLists == NULL)
+  {
+    ExFreePool(handleTable);
+    return NULL;
+  }
 
-static LOGFONTW DeviceDefaultFont =
-{ 14, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET,
-  0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS, L"" };
+  for(ObjType = 0; ObjType < OBJTYPE_COUNT; ObjType++)
+  {
+    ExInitializePagedLookasideList(handleTable->LookasideLists + ObjType, NULL, NULL, 0,
+                                   ObjInfo[ObjType].Size + sizeof(GDIOBJHDR), TAG_GDIOBJ, 0);
+  }
 
-static LOGFONTW SystemFixedFont =
-{ 14, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET,
-  0, 0, DEFAULT_QUALITY, FIXED_PITCH | FF_MODERN, L"" };
+  ShortDelay.QuadPart = -5000LL; /* FIXME - 0.5 ms? */
 
-/* FIXME: Is this correct? */
-static LOGFONTW DefaultGuiFont =
-{ 14, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET,
-  0, 0, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS, L"MS Sans Serif" };
+  return handleTable;
+}
 
-#define NB_STOCK_OBJECTS (DEFAULT_GUI_FONT + 1)
+static inline PPAGED_LOOKASIDE_LIST
+FindLookasideList(DWORD ObjectType)
+{
+  int Index;
 
-static HGDIOBJ *StockObjects[NB_STOCK_OBJECTS];
-static PGDI_HANDLE_TABLE  HandleTable = 0;
-static FAST_MUTEX  HandleTableMutex;
-static FAST_MUTEX  RefCountHandling;
+  for (Index = 0; Index < OBJTYPE_COUNT; Index++)
+  {
+    if (ObjInfo[Index].Type == ObjectType)
+    {
+      return HandleTable->LookasideLists + Index;
+    }
+  }
 
-/*!
- * 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)
-{
-  PGDI_HANDLE_TABLE  handleTable;
-
-  /* prevent APC delivery for the *FastMutexUnsafe calls */
-  const KIRQL PrevIrql = KfRaiseIrql(APC_LEVEL);
-  ExAcquireFastMutexUnsafe (&HandleTableMutex);
-  handleTable = ExAllocatePool(PagedPool,
-                               sizeof(GDI_HANDLE_TABLE) +
-                               sizeof(PGDIOBJ) * Size);
-  ASSERT( handleTable );
-  memset (handleTable,
-          0,
-          sizeof(GDI_HANDLE_TABLE) + sizeof(PGDIOBJ) * Size);
-  handleTable->wTableSize = Size;
-  ExReleaseFastMutexUnsafe (&HandleTableMutex);
-  KfLowerIrql(PrevIrql);
+  DPRINT1("Can't find lookaside list for object type 0x%08x\n", ObjectType);
 
-  return handleTable;
+  return NULL;
 }
 
-/*!
- * Returns the entry into the handle table by index.
-*/
-static PGDIOBJHDR FASTCALL
-GDIOBJ_iGetObjectForIndex(WORD TableIndex)
+static inline BOOL
+RunCleanupCallback(PGDIOBJ pObj, 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 ((GDICLEANUPPROC)ObjInfo[Index].CleanupProc)(pObj);
     }
+  }
 
-  return HandleTable->Handles[TableIndex];
+  DPRINT1("Can't find cleanup callback for object type 0x%08x\n", ObjectType);
+  return TRUE;
 }
 
-/*!
- * 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 ULONG
+GetObjectSize(DWORD ObjectType)
 {
-  WORD tableIndex;
+  int Index;
 
-  /* prevent APC delivery for the *FastMutexUnsafe calls */
-  const KIRQL PrevIrql = KfRaiseIrql(APC_LEVEL);
-  ExAcquireFastMutexUnsafe (&HandleTableMutex);
-  for (tableIndex = 1; tableIndex < HandleTable->wTableSize; tableIndex++)
+  for (Index = 0; Index < OBJTYPE_COUNT; Index++)
+  {
+    if (ObjInfo[Index].Type == ObjectType)
     {
-      if (NULL == HandleTable->Handles[tableIndex])
-       {
-         HandleTable->Handles[tableIndex] = (PGDIOBJHDR) -1;
-         break;
-       }
+      return ObjInfo[Index].Size;
     }
-  ExReleaseFastMutexUnsafe (&HandleTableMutex);
-  KfLowerIrql(PrevIrql);
+  }
 
-  return (tableIndex < HandleTable->wTableSize) ? tableIndex : 0;
+  DPRINT1("Can't find size for object type 0x%08x\n", ObjectType);
+  return 0;
 }
 
+#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;
-  
-  Index = GDIOBJ_iGetNextOpenHandleIndex ();
-  if (0 == Index)
-    {
-      DPRINT1("Out of GDI handles\n");
-      return NULL;
-    }
+  PPAGED_LOOKASIDE_LIST LookasideList;
+  LONG CurrentProcessId, LockedProcessId;
+#ifdef GDI_DEBUG
+  ULONG Attempts = 0;
+#endif
 
-  DPRINT("GDIOBJ_AllocObj: handle: %d, size: %d, type: 0x%08x\n", Index, Size, ObjectType);
-  newObject = ExAllocatePool(PagedPool, Size + sizeof (GDIOBJHDR));
-  if (newObject == NULL)
-  {
-    DPRINT1("GDIOBJ_AllocObj: failed\n");
+  W32Process = PsGetWin32Process();
+  /* HACK HACK HACK: simplest-possible quota implementation - don't allow a process
+     to take too many GDI objects, itself. */
+  if ( W32Process && W32Process->GDIObjects >= 0x2710 )
     return NULL;
+
+  ASSERT(ObjectType != GDI_OBJECT_TYPE_DONTCARE);
+
+  LookasideList = FindLookasideList(ObjectType);
+  if(LookasideList != NULL)
+  {
+    newObject = ExAllocateFromPagedLookasideList(LookasideList);
+    if(newObject != NULL)
+    {
+      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;
+
+      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);
+
+      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;
+
+          /* 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
+                       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 */
+
+          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");
+    }
   }
-  RtlZeroMemory (newObject, Size + sizeof(GDIOBJHDR));
-
-  newObject->wTableIndex = Index;
-
-  newObject->dwCount = 0;
-  newObject->hProcessId = PsGetCurrentProcessId ();
-  newObject->CleanupProc = CleanupProc;
-  newObject->Magic = GDI_TYPE_TO_MAGIC(ObjectType);
-  newObject->lockfile = NULL;
-  newObject->lockline = 0;
-  HandleTable->Handles[Index] = newObject;
-  
-  W32Process = PsGetCurrentProcess()->Win32Process;
-  if(W32Process)
+  else
   {
-    W32Process->GDIObjects++;
+    DPRINT1("Failed to find lookaside list for object type 0x%x\n", ObjectType);
   }
-
-  return GDI_HANDLE_CREATE(Index, ObjectType);
+  return NULL;
 }
 
 /*!
@@ -281,68 +447,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;
-  BOOL         bRet = TRUE;
+  PGDI_TABLE_ENTRY Entry;
+  PPAGED_LOOKASIDE_LIST LookasideList;
+  LONG ProcessId, LockedProcessId, PrevProcId, ExpectedType;
+#ifdef GDI_DEBUG
+  ULONG Attempts = 0;
+#endif
+
+  DPRINT("GDIOBJ_FreeObj: hObj: 0x%08x\n", hObj);
+
+  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;
+  }
 
-  objectHeader = GDIOBJ_iGetObjectForIndex(GDI_HANDLE_GET_INDEX(hObj));
-  DPRINT("GDIOBJ_FreeObj: hObj: 0x%08x, object: %x\n", hObj, objectHeader);
+  /* 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 (! GDI_VALID_OBJECT(hObj, objectHeader, ObjectType, Flag)
-      || GDI_GLOBAL_PROCESS == objectHeader->hProcessId)
+  ExpectedType = ((ObjectType != GDI_OBJECT_TYPE_DONTCARE) ? ObjectType : 0);
 
+  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)))
     {
-      DPRINT1("Can't delete hObj:0x%08x, type:0x%08x, flag:%d\n", hObj, ObjectType, Flag);
-      return FALSE;
+      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;
+
+        /* 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;
+      }
     }
-
-  DPRINT("FreeObj: locks: %x\n", objectHeader->dwCount );
-  if (!(Flag & GDIOBJFLAG_IGNORELOCK))
+    else
     {
-      /* 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);
+      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);
     }
-
-  /* allow object to delete internal data */
-  if (NULL != objectHeader->CleanupProc)
+  }
+  else if(PrevProcId == LockedProcessId)
+  {
+#ifdef GDI_DEBUG
+    if(++Attempts > 20)
     {
-      Obj = (PGDIOBJ)((PCHAR)objectHeader + sizeof(GDIOBJHDR));
-      bRet = (*(objectHeader->CleanupProc))(Obj);
+      DPRINT1("[%d]Waiting on 0x%x\n", Attempts, hObj);
     }
-
-  ExFreePool(objectHeader);
-  HandleTable->Handles[GDI_HANDLE_GET_INDEX(hObj)] = NULL;
-  
-  W32Process = PsGetCurrentProcess()->Win32Process;
-  if(W32Process)
+#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
   {
-    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;
 }
 
 /*!
@@ -355,7 +600,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;
@@ -397,7 +642,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;
@@ -416,7 +661,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;
        }
     }
@@ -424,165 +669,16 @@ GDIOBJ_UnlockMultipleObj(PGDIMULTILOCK pList, INT nObj)
   return TRUE;
 }
 
-/*!
- * Marks the object as global. (Creator process ID is set to GDI_GLOBAL_PROCESS). Global objects may be
- * accessed by any process.
- * \param      ObjectHandle - handle of the object to make global.
- *
- * \note       Only stock objects should be marked global.
-*/
-VOID FASTCALL
-GDIOBJ_MarkObjectGlobal(HGDIOBJ ObjectHandle)
-{
-  PEPROCESS Process;
-  PW32PROCESS W32Process;
-  NTSTATUS Status;
-  PGDIOBJHDR ObjHdr;
-
-  DPRINT("GDIOBJ_MarkObjectGlobal handle 0x%08x\n", ObjectHandle);
-  ObjHdr = GDIOBJ_iGetObjectForIndex(GDI_HANDLE_GET_INDEX(ObjectHandle));
-  if (NULL == ObjHdr)
-    {
-      return;
-    }
-  
-  Status = PsLookupProcessByProcessId((PVOID)ObjHdr->hProcessId, &Process);
-  if(NT_SUCCESS(Status))
-  {
-    W32Process = Process->Win32Process;
-    if(W32Process)
-    {
-      W32Process->GDIObjects--;
-    }
-  ObDereferenceObject(Process);
-  }
-  
-  ObjHdr->hProcessId = GDI_GLOBAL_PROCESS;
-}
-
-/*!
- * Removes the global mark from the object. Global objects may be
- * accessed by any process.
- * \param      ObjectHandle - handle of the object to make local.
- *
- * \note       Only stock objects should be marked global.
-*/
-VOID FASTCALL
-GDIOBJ_UnmarkObjectGlobal(HGDIOBJ ObjectHandle)
-{
-  PW32PROCESS W32Process;
-  PGDIOBJHDR ObjHdr;
-
-  DPRINT("GDIOBJ_MarkObjectGlobal handle 0x%08x\n", ObjectHandle);
-  ObjHdr = GDIOBJ_iGetObjectForIndex(GDI_HANDLE_GET_INDEX(ObjectHandle));
-  if (NULL == ObjHdr /*|| GDI_GLOBAL_PROCESS != ObjHdr->hProcessId*/)
-    {
-      return;
-    }
-  
-  W32Process = PsGetCurrentProcess()->Win32Process;
-  if(W32Process)
-  {
-    W32Process->GDIObjects++;
-  }
-  
-  ObjHdr->hProcessId = PsGetCurrentProcessId();
-}
-
-/*!
- * 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] =  NtGdiCreateBrushIndirect(&WhiteBrush);
-  StockObjects[LTGRAY_BRUSH] = NtGdiCreateBrushIndirect(&LtGrayBrush);
-  StockObjects[GRAY_BRUSH] =   NtGdiCreateBrushIndirect(&GrayBrush);
-  StockObjects[DKGRAY_BRUSH] = NtGdiCreateBrushIndirect(&DkGrayBrush);
-  StockObjects[BLACK_BRUSH] =  NtGdiCreateBrushIndirect(&BlackBrush);
-  StockObjects[NULL_BRUSH] =   NtGdiCreateBrushIndirect(&NullBrush);
-
-  StockObjects[WHITE_PEN] = NtGdiCreatePenIndirect(&WhitePen);
-  StockObjects[BLACK_PEN] = NtGdiCreatePenIndirect(&BlackPen);
-  StockObjects[NULL_PEN] =  NtGdiCreatePenIndirect(&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_MarkObjectGlobal(StockObjects[Object]);
-/*       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);
 }
 
 /*!
@@ -595,19 +691,22 @@ NtGdiDeleteObject(HGDIOBJ hObject)
 {
   DPRINT("NtGdiDeleteObject handle 0x%08x\n", hObject);
 
-  return GDIOBJ_FreeObj(hObject, GDI_OBJECT_TYPE_DONTCARE, GDIOBJFLAG_DEFAULT);
+  return NULL != hObject
+         ? 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();
@@ -615,19 +714,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)
     {
@@ -639,188 +759,731 @@ CleanupForProcess (struct _EPROCESS *Process, INT Pid)
   return TRUE;
 }
 
-#define GDIOBJ_TRACKLOCKS
-
-#ifdef GDIOBJ_LockObj
-#undef GDIOBJ_LockObj
-PGDIOBJ FASTCALL
-GDIOBJ_LockObjDbg (const char* file, int line, HGDIOBJ hObj, DWORD ObjectType)
-{
-  PGDIOBJ rc;
-  PGDIOBJHDR ObjHdr = GDIOBJ_iGetObjectForIndex(GDI_HANDLE_GET_INDEX(hObj));
-
-  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;
-    }
-  if (NULL != ObjHdr->lockfile)
-    {
-      DPRINT1("Caution! GDIOBJ_LockObj trying to lock object (0x%x) second time\n", hObj );
-      DPRINT1("\tcalled from: %s:%i\n", file, line );
-      DPRINT1("\tpreviously locked from: %s:%i\n", ObjHdr->lockfile, ObjHdr->lockline );
-    }
-  DPRINT("(%s:%i) GDIOBJ_LockObj(0x%08x,0x%08x)\n", file, line, hObj, ObjectType);
-  rc = GDIOBJ_LockObj(hObj, ObjectType);
-  if (rc && NULL == ObjHdr->lockfile)
-    {
-      ObjHdr->lockfile = file;
-      ObjHdr->lockline = line;
-    }
-
-  return rc;
-}
-#endif//GDIOBJ_LockObj
-
-#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));
-
-  if (! GDI_VALID_OBJECT(hObj, ObjHdr, ObjectType, GDIOBJFLAG_DEFAULT))
-    {
-      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;
-
-  return GDIOBJ_UnlockObj(hObj, ObjectType);
-}
-#endif//GDIOBJ_LockObj
-
 /*!
  * 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!
+ * \todo Get rid of the ObjectType parameter!
 */
-PGDIOBJ FASTCALL
-GDIOBJ_LockObj(HGDIOBJ hObj, DWORD ObjectType)
+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("GDIOBJ_LockObj: hObj: 0x%08x\n", hObj);
+
+  Thread = PsGetCurrentThread();
+
+  /* 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;
+
+  ExpectedType = ((ObjectType != GDI_OBJECT_TYPE_DONTCARE) ? ObjectType : 0);
+
+  Entry = GDI_HANDLE_GET_ENTRY(HandleTable, hObj);
 
-  DPRINT("GDIOBJ_LockObj: hObj: 0x%08x, type: 0x%08x, objhdr: %x\n", hObj, ObjectType, ObjHdr);
-  if (! GDI_VALID_OBJECT(hObj, ObjHdr, ObjectType, GDIOBJFLAG_DEFAULT))
+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;
+
+    /* 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_LockObj failed for 0x%08x, type 0x%08x\n",
-                 hObj, ObjectType);
-      return NULL;
-    }
+      PETHREAD PrevThread;
+      PGDIOBJHDR GdiHdr;
+
+      GdiHdr = GDIBdyToHdr(Entry->KernelData);
+
+      /* 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);
+
+      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
 
-  if(0 < ObjHdr->dwCount)
+        DelayExecution();
+        goto LockHandle;
+      }
+    }
+    else
+    {
+      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("Caution! GDIOBJ_LockObj trying to lock object (0x%x) second time\n", hObj);
-      DPRINT1("\t called from: %x\n", __builtin_return_address(0));
+      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(&RefCountHandling);
-  ObjHdr->dwCount++;
-  ExReleaseFastMutex(&RefCountHandling);
-  return (PGDIOBJ)((PCHAR)ObjHdr + sizeof(GDIOBJHDR));
+    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
+  }
+
+  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();
+
+  /* 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);
 
-  ExAcquireFastMutex(&RefCountHandling);
-  if (0 == (ObjHdr->dwCount & ~0x80000000))
+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);
+
+      PrevThread = GdiHdr->LockingThread;
+      if(PrevThread == Thread)
+      {
+        BOOL Ret;
+
+        if(--GdiHdr->Locks == 0)
+        {
+          GdiHdr->LockingThread = NULL;
 
-  ObjHdr->dwCount--;
+#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);
+
+          ASSERT(ProcessId != 0); /* must not delete a global handle!!!! */
+
+          /* 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);
 
-  if (ObjHdr->dwCount == 0x80000000)
+        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_TakeOwnership(HGDIOBJ ObjectHandle)
+BOOL INTERNAL_CALL
+GDIOBJ_ConvertToStockObj(HGDIOBJ *hObj)
 {
-  PGDIOBJHDR ObjHdr = GDIOBJ_iGetObjectForIndex(GDI_HANDLE_GET_INDEX(ObjectHandle));
+/*
+ * 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);
+
+  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)
+    {
+      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)
+        {
+          /* 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 = OldProcess->Win32Process;
+              if(W32Process != NULL)
+              {
+                InterlockedDecrement(&W32Process->GDIObjects);
+              }
+              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);
+
+          DelayExecution();
+          goto LockHandle;
+        }
+      }
+      else
+      {
+        DPRINT1("Attempted to convert object 0x%x that is deleted! Should never get here!!!\n", hObj);
+      }
+    }
+    else if(PrevProcId == LockedProcessId)
+    {
+#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
+    {
+      DPRINT1("Attempted to convert invalid handle: 0x%x\n", hObj);
+    }
+  }
+
+  return FALSE;
+}
+
+void INTERNAL_CALL
+GDIOBJ_SetOwnership(HGDIOBJ ObjectHandle, PEPROCESS NewOwner)
+{
+  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));
 
-  if (GDI_GLOBAL_PROCESS != ObjHdr->hProcessId)
+  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;
+
+    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)
+    {
+      PETHREAD PrevThread;
+
+      if(Entry->Type != 0 && Entry->KernelData != NULL)
+      {
+        PGDIOBJHDR GdiHdr = GDIBdyToHdr(Entry->KernelData);
+
+        PrevThread = GdiHdr->LockingThread;
+        if(PrevThread == NULL || PrevThread == Thread)
+        {
+          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())
     {
-      ObjHdr->hProcessId = 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)
+        {
+          /* 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, 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)
+            {
+              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 */