dump some statistics on the gdi handle table when it runs out of handles
[reactos.git] / reactos / subsys / win32k / objects / gdiobj.c
1 /*
2 * ReactOS W32 Subsystem
3 * Copyright (C) 1998 - 2004 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 /*
20 * GDIOBJ.C - GDI object manipulation routines
21 *
22 * $Id: gdiobj.c,v 1.79 2004/12/18 21:41:17 royce Exp $
23 */
24 #include <w32k.h>
25
26 #define NDEBUG
27 #include <debug.h>
28
29 #ifdef __USE_W32API
30 /* F*(&#$ header mess!!!! */
31 HANDLE
32 STDCALL PsGetProcessId(
33 PEPROCESS Process
34 );
35 #endif /* __USE_W32API */
36
37
38
39
40 #define GDI_ENTRY_TO_INDEX(ht, e) \
41 (((ULONG_PTR)(e) - (ULONG_PTR)&((ht)->Entries[0])) / sizeof(GDI_TABLE_ENTRY))
42 #define GDI_HANDLE_GET_ENTRY(HandleTable, h) \
43 (&(HandleTable)->Entries[GDI_HANDLE_GET_INDEX((h))])
44
45 #define GDIBdyToHdr(body) \
46 ((PGDIOBJHDR)(body) - 1)
47 #define GDIHdrToBdy(hdr) \
48 (PGDIOBJ)((PGDIOBJHDR)(hdr) + 1)
49
50 /* apparently the first 10 entries are never used in windows as they are empty */
51 #define RESERVE_ENTRIES_COUNT 10
52
53 typedef struct _GDI_HANDLE_TABLE
54 {
55 PPAGED_LOOKASIDE_LIST LookasideLists;
56
57 SLIST_HEADER FreeEntriesHead;
58 SLIST_ENTRY FreeEntries[((GDI_HANDLE_COUNT * sizeof(GDI_TABLE_ENTRY)) << 3) /
59 (sizeof(SLIST_ENTRY) << 3)];
60
61 GDI_TABLE_ENTRY Entries[GDI_HANDLE_COUNT];
62 } GDI_HANDLE_TABLE, *PGDI_HANDLE_TABLE;
63
64 typedef struct
65 {
66 ULONG Type;
67 ULONG Size;
68 GDICLEANUPPROC CleanupProc;
69 } GDI_OBJ_INFO, *PGDI_OBJ_INFO;
70
71 /*
72 * Dummy GDI Cleanup Callback
73 */
74 BOOL INTERNAL_CALL
75 GDI_CleanupDummy(PVOID ObjectBody)
76 {
77 return TRUE;
78 }
79
80 /* Testing shows that regions are the most used GDIObj type,
81 so put that one first for performance */
82 const
83 GDI_OBJ_INFO ObjInfo[] =
84 {
85 /* Type */ /* Size */ /* CleanupProc */
86 {GDI_OBJECT_TYPE_REGION, sizeof(ROSRGNDATA), RGNDATA_Cleanup},
87 {GDI_OBJECT_TYPE_BITMAP, sizeof(BITMAPOBJ), BITMAP_Cleanup},
88 {GDI_OBJECT_TYPE_DC, sizeof(DC), DC_Cleanup},
89 {GDI_OBJECT_TYPE_PALETTE, sizeof(PALGDI), PALETTE_Cleanup},
90 {GDI_OBJECT_TYPE_BRUSH, sizeof(GDIBRUSHOBJ), BRUSH_Cleanup},
91 {GDI_OBJECT_TYPE_PEN, sizeof(GDIBRUSHOBJ), GDI_CleanupDummy},
92 {GDI_OBJECT_TYPE_FONT, sizeof(TEXTOBJ), GDI_CleanupDummy},
93 {GDI_OBJECT_TYPE_DCE, sizeof(DCE), DCE_Cleanup},
94 /*{GDI_OBJECT_TYPE_DIRECTDRAW, sizeof(DD_DIRECTDRAW), DD_Cleanup},
95 {GDI_OBJECT_TYPE_DD_SURFACE, sizeof(DD_SURFACE), DDSURF_Cleanup},*/
96 {GDI_OBJECT_TYPE_EXTPEN, 0, GDI_CleanupDummy},
97 {GDI_OBJECT_TYPE_METADC, 0, GDI_CleanupDummy},
98 {GDI_OBJECT_TYPE_METAFILE, 0, GDI_CleanupDummy},
99 {GDI_OBJECT_TYPE_ENHMETAFILE, 0, GDI_CleanupDummy},
100 {GDI_OBJECT_TYPE_ENHMETADC, 0, GDI_CleanupDummy},
101 {GDI_OBJECT_TYPE_MEMDC, 0, GDI_CleanupDummy},
102 {GDI_OBJECT_TYPE_EMF, 0, GDI_CleanupDummy}
103 };
104
105 #define OBJTYPE_COUNT (sizeof(ObjInfo) / sizeof(ObjInfo[0]))
106
107 static PGDI_HANDLE_TABLE HandleTable = NULL;
108 static LARGE_INTEGER ShortDelay;
109
110 #define DelayExecution() \
111 DPRINT("%s:%i: Delay\n", __FILE__, __LINE__); \
112 KeDelayExecutionThread(KernelMode, FALSE, &ShortDelay)
113
114 /*!
115 * Allocate GDI object table.
116 * \param Size - number of entries in the object table.
117 */
118 static PGDI_HANDLE_TABLE INTERNAL_CALL
119 GDIOBJ_iAllocHandleTable(VOID)
120 {
121 PGDI_HANDLE_TABLE handleTable;
122 UINT ObjType;
123 UINT i;
124 PGDI_TABLE_ENTRY Entry;
125
126 handleTable = ExAllocatePoolWithTag(NonPagedPool, sizeof(GDI_HANDLE_TABLE), TAG_GDIHNDTBLE);
127 ASSERT( handleTable );
128 RtlZeroMemory(handleTable, sizeof(GDI_HANDLE_TABLE));
129
130 /*
131 * initialize the free entry cache
132 */
133 InitializeSListHead(&handleTable->FreeEntriesHead);
134 Entry = &HandleTable->Entries[RESERVE_ENTRIES_COUNT];
135 for(i = GDI_HANDLE_COUNT - 1; i >= RESERVE_ENTRIES_COUNT; i--)
136 {
137 InterlockedPushEntrySList(&handleTable->FreeEntriesHead, &handleTable->FreeEntries[i]);
138 }
139
140 handleTable->LookasideLists = ExAllocatePoolWithTag(NonPagedPool,
141 OBJTYPE_COUNT * sizeof(PAGED_LOOKASIDE_LIST),
142 TAG_GDIHNDTBLE);
143 if(handleTable->LookasideLists == NULL)
144 {
145 ExFreePool(handleTable);
146 return NULL;
147 }
148
149 for(ObjType = 0; ObjType < OBJTYPE_COUNT; ObjType++)
150 {
151 ExInitializePagedLookasideList(handleTable->LookasideLists + ObjType, NULL, NULL, 0,
152 ObjInfo[ObjType].Size + sizeof(GDIOBJHDR), TAG_GDIOBJ, 0);
153 }
154
155 ShortDelay.QuadPart = -5000LL; /* FIXME - 0.5 ms? */
156
157 return handleTable;
158 }
159
160 static inline PPAGED_LOOKASIDE_LIST
161 FindLookasideList(DWORD ObjectType)
162 {
163 int Index;
164
165 for (Index = 0; Index < OBJTYPE_COUNT; Index++)
166 {
167 if (ObjInfo[Index].Type == ObjectType)
168 {
169 return HandleTable->LookasideLists + Index;
170 }
171 }
172
173 DPRINT1("Can't find lookaside list for object type 0x%08x\n", ObjectType);
174
175 return NULL;
176 }
177
178 static inline BOOL
179 RunCleanupCallback(PGDIOBJ pObj, DWORD ObjectType)
180 {
181 int Index;
182
183 for (Index = 0; Index < OBJTYPE_COUNT; Index++)
184 {
185 if (ObjInfo[Index].Type == ObjectType)
186 {
187 return ((GDICLEANUPPROC)ObjInfo[Index].CleanupProc)(pObj);
188 }
189 }
190
191 DPRINT1("Can't find cleanup callback for object type 0x%08x\n", ObjectType);
192 return TRUE;
193 }
194
195 static inline ULONG
196 GetObjectSize(DWORD ObjectType)
197 {
198 int Index;
199
200 for (Index = 0; Index < OBJTYPE_COUNT; Index++)
201 {
202 if (ObjInfo[Index].Type == ObjectType)
203 {
204 return ObjInfo[Index].Size;
205 }
206 }
207
208 DPRINT1("Can't find size for object type 0x%08x\n", ObjectType);
209 return 0;
210 }
211
212 #ifdef DBG
213
214 static int leak_reported = 0;
215 #define GDI_STACK_LEVELS 4
216 static ULONG GDIHandleAllocator[GDI_STACK_LEVELS][GDI_HANDLE_COUNT];
217 struct DbgOpenGDIHandle
218 {
219 ULONG loc;
220 int count;
221 };
222 #define H 1024
223 static struct DbgOpenGDIHandle h[H];
224
225 void IntDumpHandleTable ( int which )
226 {
227 int i, n = 0, j;
228
229 // step through GDI handle table and find out who our culprit is...
230 for ( i = RESERVE_ENTRIES_COUNT; i < GDI_HANDLE_COUNT; i++ )
231 {
232 for ( j = 0; j < n; j++ )
233 {
234 if ( GDIHandleAllocator[which][i] == h[j].loc )
235 break;
236 }
237 if ( j < H )
238 {
239 if ( j == n )
240 {
241 h[j].loc = GDIHandleAllocator[which][i];
242 h[j].count = 1;
243 n = n + 1;
244 }
245 else
246 h[j].count++;
247 }
248 }
249 // bubble sort time! weeeeee!!
250 for ( i = 0; i < n-1; i++ )
251 {
252 if ( h[i].count < h[i+1].count )
253 {
254 struct DbgOpenGDIHandle t;
255 t.loc = h[i+1].loc;
256 t.count = h[i+1].count;
257 h[i+1].loc = h[i].loc;
258 h[i+1].count = h[i].count;
259 j = i;
260 while ( j > 0 && h[j-1].count < t.count )
261 j--;
262 h[j] = t;
263 }
264 }
265 // print the first 30 offenders...
266 DbgPrint ( "Worst GDI Handle leak offenders - stack trace level %i (out of %i unique locations):\n", which, n );
267 for ( i = 0; i < 30 && i < n; i++ )
268 {
269 DbgPrint ( "\t" );
270 if ( !KeRosPrintAddress ( (PVOID)h[i].loc ) )
271 DbgPrint ( "<%X>", h[i].loc );
272 DbgPrint ( " (%i allocations)\n", h[i].count );
273 }
274 }
275 #endif//DBG
276
277 /*!
278 * Allocate memory for GDI object and return handle to it.
279 *
280 * \param ObjectType - type of object \ref GDI object types
281 *
282 * \return Handle of the allocated object.
283 *
284 * \note Use GDIOBJ_Lock() to obtain pointer to the new object.
285 * \todo return the object pointer and lock it by default.
286 */
287 HGDIOBJ INTERNAL_CALL
288 #ifdef GDI_DEBUG
289 GDIOBJ_AllocObjDbg(const char* file, int line, ULONG ObjectType)
290 #else /* !GDI_DEBUG */
291 GDIOBJ_AllocObj(ULONG ObjectType)
292 #endif /* GDI_DEBUG */
293 {
294 PW32PROCESS W32Process;
295 PGDIOBJHDR newObject;
296 PPAGED_LOOKASIDE_LIST LookasideList;
297 LONG CurrentProcessId, LockedProcessId;
298 #ifdef GDI_DEBUG
299 ULONG Attempts = 0;
300 #endif
301
302 ASSERT(ObjectType != GDI_OBJECT_TYPE_DONTCARE);
303
304 LookasideList = FindLookasideList(ObjectType);
305 if(LookasideList != NULL)
306 {
307 newObject = ExAllocateFromPagedLookasideList(LookasideList);
308 if(newObject != NULL)
309 {
310 PSLIST_ENTRY FreeEntry;
311 PGDI_TABLE_ENTRY Entry;
312 PGDIOBJ ObjectBody;
313 LONG TypeInfo;
314
315 /* shift the process id to the left so we can use the first bit to lock
316 the object.
317 FIXME - don't shift once ROS' PIDs match with nt! */
318 CurrentProcessId = (LONG)PsGetCurrentProcessId() << 1;
319 LockedProcessId = CurrentProcessId | 0x1;
320 W32Process = PsGetWin32Process();
321
322 newObject->LockingThread = NULL;
323 newObject->Locks = 0;
324
325 #ifdef GDI_DEBUG
326 newObject->createdfile = file;
327 newObject->createdline = line;
328 newObject->lockfile = NULL;
329 newObject->lockline = 0;
330 #endif
331
332 ObjectBody = GDIHdrToBdy(newObject);
333
334 RtlZeroMemory(ObjectBody, GetObjectSize(ObjectType));
335
336 TypeInfo = (ObjectType & 0xFFFF0000) | (ObjectType >> 16);
337
338 FreeEntry = InterlockedPopEntrySList(&HandleTable->FreeEntriesHead);
339 if(FreeEntry != NULL)
340 {
341 LONG PrevProcId;
342 UINT Index;
343 HGDIOBJ Handle;
344
345 /* calculate the entry from the address of the entry in the free slot array */
346 Index = ((ULONG_PTR)FreeEntry - (ULONG_PTR)&HandleTable->FreeEntries[0]) /
347 sizeof(HandleTable->FreeEntries[0]);
348 Entry = &HandleTable->Entries[Index];
349 Handle = (HGDIOBJ)((Index & 0xFFFF) | (ObjectType & 0xFFFF0000));
350
351 LockHandle:
352 PrevProcId = InterlockedCompareExchange(&Entry->ProcessId, LockedProcessId, 0);
353 if(PrevProcId == 0)
354 {
355 ASSERT(Entry->KernelData == NULL);
356
357 Entry->KernelData = ObjectBody;
358
359 /* we found a free entry, no need to exchange this field atomically
360 since we're holding the lock */
361 Entry->Type = TypeInfo;
362
363 /* unlock the entry */
364 InterlockedExchange(&Entry->ProcessId, CurrentProcessId);
365
366 #ifdef DBG
367 {
368 PULONG Frame;
369 int which;
370 #if defined __GNUC__
371 __asm__("mov %%ebp, %%ebx" : "=b" (Frame) : );
372 #elif defined(_MSC_VER)
373 __asm mov [Frame], ebp
374 #endif
375 Frame = (PULONG)Frame[0]; // step out of AllocObj()
376 for ( which = 0; which < GDI_STACK_LEVELS && Frame[1] != 0 && Frame[1] != 0xDEADBEEF; which++ )
377 {
378 GDIHandleAllocator[which][Index] = Frame[1]; // step out of AllocObj()
379 Frame = ((PULONG)Frame[0]);
380 }
381 for ( ; which < GDI_STACK_LEVELS; which++ )
382 GDIHandleAllocator[which][Index] = 0xDEADBEEF;
383 }
384 #endif//DBG
385 //ExAllocatePool ( PagedPool, (ULONG)newObject ); // initiate red-zone verification of object we allocated
386
387 if(W32Process != NULL)
388 {
389 InterlockedIncrement(&W32Process->GDIObjects);
390 }
391
392 DPRINT("GDIOBJ_AllocObj: 0x%x ob: 0x%x\n", Handle, ObjectBody);
393 return Handle;
394 }
395 else
396 {
397 #ifdef GDI_DEBUG
398 if(++Attempts > 20)
399 {
400 DPRINT1("[%d]Waiting on 0x%x\n", Attempts, Handle);
401 }
402 #endif
403 /* damn, someone is trying to lock the object even though it doesn't
404 eve nexist anymore, wait a little and try again!
405 FIXME - we shouldn't loop forever! Give up after some time! */
406 DelayExecution();
407 /* try again */
408 goto LockHandle;
409 }
410 }
411
412 ExFreeToPagedLookasideList(LookasideList, newObject);
413 DPRINT1("Failed to insert gdi object into the handle table, no handles left!\n");
414 #ifdef DBG
415 if ( !leak_reported )
416 {
417 DPRINT1("reporting gdi handle abusers:\n");
418 int which;
419 for ( which = 0; which < GDI_STACK_LEVELS; which++ )
420 IntDumpHandleTable(which);
421 leak_reported = 1;
422 }
423 else
424 {
425 DPRINT1("gdi handle abusers already reported!\n");
426 }
427 #endif//DBG
428 }
429 else
430 {
431 DPRINT1("Not enough memory to allocate gdi object!\n");
432 }
433 }
434 else
435 {
436 DPRINT1("Failed to find lookaside list for object type 0x%x\n", ObjectType);
437 }
438 return NULL;
439 }
440
441 /*!
442 * Free memory allocated for the GDI object. For each object type this function calls the
443 * appropriate cleanup routine.
444 *
445 * \param hObj - handle of the object to be deleted.
446 *
447 * \return Returns TRUE if succesful.
448 * \return Returns FALSE if the cleanup routine returned FALSE or the object doesn't belong
449 * to the calling process.
450 */
451 BOOL INTERNAL_CALL
452 #ifdef GDI_DEBUG
453 GDIOBJ_FreeObjDbg(const char* file, int line, HGDIOBJ hObj, DWORD ObjectType)
454 #else /* !GDI_DEBUG */
455 GDIOBJ_FreeObj(HGDIOBJ hObj, DWORD ObjectType)
456 #endif /* GDI_DEBUG */
457 {
458 PGDI_TABLE_ENTRY Entry;
459 PPAGED_LOOKASIDE_LIST LookasideList;
460 LONG ProcessId, LockedProcessId, PrevProcId, ExpectedType;
461 #ifdef GDI_DEBUG
462 ULONG Attempts = 0;
463 #endif
464
465 DPRINT("GDIOBJ_FreeObj: hObj: 0x%08x\n", hObj);
466
467 if(GDI_HANDLE_IS_STOCKOBJ(hObj))
468 {
469 DPRINT1("GDIOBJ_FreeObj() failed, can't delete stock object handle: 0x%x !!!\n", hObj);
470 #ifdef GDI_DEBUG
471 DPRINT1("-> called from %s:%i\n", file, line);
472 #endif
473 return FALSE;
474 }
475
476 /* shift the process id to the left so we can use the first bit to lock the object.
477 FIXME - don't shift once ROS' PIDs match with nt! */
478 ProcessId = (LONG)PsGetCurrentProcessId() << 1;
479 LockedProcessId = ProcessId | 0x1;
480
481 ExpectedType = ((ObjectType != GDI_OBJECT_TYPE_DONTCARE) ? ObjectType : 0);
482
483 Entry = GDI_HANDLE_GET_ENTRY(HandleTable, hObj);
484
485 LockHandle:
486 /* lock the object, we must not delete global objects, so don't exchange the locking
487 process ID to zero when attempting to lock a global object... */
488 PrevProcId = InterlockedCompareExchange(&Entry->ProcessId, LockedProcessId, ProcessId);
489 if(PrevProcId == ProcessId)
490 {
491 if(Entry->Type != 0 && Entry->KernelData != NULL && (ExpectedType == 0 || ((Entry->Type << 16) == ExpectedType)))
492 {
493 PGDIOBJHDR GdiHdr;
494
495 GdiHdr = GDIBdyToHdr(Entry->KernelData);
496
497 if(GdiHdr->LockingThread == NULL)
498 {
499 BOOL Ret;
500 PW32PROCESS W32Process = PsGetWin32Process();
501 ULONG Type = Entry->Type << 16;
502
503 /* Clear the type field so when unlocking the handle it gets finally deleted */
504 Entry->Type = 0;
505 Entry->KernelData = NULL;
506
507 /* unlock the handle slot */
508 InterlockedExchange(&Entry->ProcessId, 0);
509
510 /* push this entry to the free list */
511 InterlockedPushEntrySList(&HandleTable->FreeEntriesHead,
512 &HandleTable->FreeEntries[GDI_ENTRY_TO_INDEX(HandleTable, Entry)]);
513
514 if(W32Process != NULL)
515 {
516 InterlockedDecrement(&W32Process->GDIObjects);
517 }
518
519 /* call the cleanup routine. */
520 Ret = RunCleanupCallback(GDIHdrToBdy(GdiHdr), Type);
521
522 /* Now it's time to free the memory */
523 LookasideList = FindLookasideList(Type);
524 if(LookasideList != NULL)
525 {
526 ExFreeToPagedLookasideList(LookasideList, GdiHdr);
527 }
528
529 return Ret;
530 }
531 else
532 {
533 /* the object is currently locked. just clear the type field so when the
534 object gets unlocked it will be finally deleted from the table. */
535 Entry->Type = 0;
536
537 /* unlock the handle slot */
538 InterlockedExchange(&Entry->ProcessId, 0);
539
540 /* report a successful deletion as the object is actually removed from the table */
541 return TRUE;
542 }
543 }
544 else
545 {
546 if(Entry->Type != 0)
547 {
548 DPRINT1("Attempted to delete object 0x%x, type mismatch (0x%x : 0x%x)\n", hObj, ObjectType, ExpectedType);
549 }
550 else
551 {
552 DPRINT1("Attempted to delete object 0x%x which was already deleted!\n", hObj);
553 }
554 InterlockedExchange(&Entry->ProcessId, PrevProcId);
555 }
556 }
557 else if(PrevProcId == LockedProcessId)
558 {
559 #ifdef GDI_DEBUG
560 if(++Attempts > 20)
561 {
562 DPRINT1("[%d]Waiting on 0x%x\n", Attempts, hObj);
563 }
564 #endif
565 /* the object is currently locked, wait some time and try again.
566 FIXME - we shouldn't loop forever! Give up after some time! */
567 DelayExecution();
568 /* try again */
569 goto LockHandle;
570 }
571 else
572 {
573 if((PrevProcId >> 1) == 0)
574 {
575 DPRINT1("Attempted to free global gdi handle 0x%x, caller needs to get ownership first!!!", hObj);
576 }
577 else
578 {
579 DPRINT1("Attempted to free foreign handle: 0x%x Owner: 0x%x from Caller: 0x%x\n", hObj, PrevProcId >> 1, ProcessId >> 1);
580 }
581 #ifdef GDI_DEBUG
582 DPRINT1("-> called from %s:%i\n", file, line);
583 #endif
584 }
585
586 return FALSE;
587 }
588
589 /*!
590 * Lock multiple objects. Use this function when you need to lock multiple objects and some of them may be
591 * duplicates. You should use this function to avoid trying to lock the same object twice!
592 *
593 * \param pList pointer to the list that contains handles to the objects. You should set hObj and ObjectType fields.
594 * \param nObj number of objects to lock
595 * \return for each entry in pList this function sets pObj field to point to the object.
596 *
597 * \note this function uses an O(n^2) algoritm because we shouldn't need to call it with more than 3 or 4 objects.
598 */
599 BOOL INTERNAL_CALL
600 GDIOBJ_LockMultipleObj(PGDIMULTILOCK pList, INT nObj)
601 {
602 INT i, j;
603 ASSERT( pList );
604 /* FIXME - check for "invalid" handles */
605 /* go through the list checking for duplicate objects */
606 for (i = 0; i < nObj; i++)
607 {
608 pList[i].pObj = NULL;
609 for (j = 0; j < i; j++)
610 {
611 if (pList[i].hObj == pList[j].hObj)
612 {
613 /* already locked, so just copy the pointer to the object */
614 pList[i].pObj = pList[j].pObj;
615 break;
616 }
617 }
618
619 if (NULL == pList[i].pObj)
620 {
621 /* object hasn't been locked, so lock it. */
622 if (NULL != pList[i].hObj)
623 {
624 pList[i].pObj = GDIOBJ_LockObj(pList[i].hObj, pList[i].ObjectType);
625 }
626 }
627 }
628
629 return TRUE;
630 }
631
632 /*!
633 * Unlock multiple objects. Use this function when you need to unlock multiple objects and some of them may be
634 * duplicates.
635 *
636 * \param pList pointer to the list that contains handles to the objects. You should set hObj and ObjectType fields.
637 * \param nObj number of objects to lock
638 *
639 * \note this function uses O(n^2) algoritm because we shouldn't need to call it with more than 3 or 4 objects.
640 */
641 BOOL INTERNAL_CALL
642 GDIOBJ_UnlockMultipleObj(PGDIMULTILOCK pList, INT nObj)
643 {
644 INT i, j;
645 ASSERT(pList);
646
647 /* go through the list checking for duplicate objects */
648 for (i = 0; i < nObj; i++)
649 {
650 if (NULL != pList[i].pObj)
651 {
652 for (j = i + 1; j < nObj; j++)
653 {
654 if ((pList[i].pObj == pList[j].pObj))
655 {
656 /* set the pointer to zero for all duplicates */
657 pList[j].pObj = NULL;
658 }
659 }
660 GDIOBJ_UnlockObj(pList[i].hObj);
661 pList[i].pObj = NULL;
662 }
663 }
664
665 return TRUE;
666 }
667
668 /*!
669 * Initialization of the GDI object engine.
670 */
671 VOID INTERNAL_CALL
672 InitGdiObjectHandleTable (VOID)
673 {
674 DPRINT("InitGdiObjectHandleTable\n");
675
676 HandleTable = GDIOBJ_iAllocHandleTable();
677 DPRINT("HandleTable: %x\n", HandleTable);
678 }
679
680 /*!
681 * Delete GDI object
682 * \param hObject object handle
683 * \return if the function fails the returned value is FALSE.
684 */
685 BOOL STDCALL
686 NtGdiDeleteObject(HGDIOBJ hObject)
687 {
688 DPRINT("NtGdiDeleteObject handle 0x%08x\n", hObject);
689
690 return NULL != hObject
691 ? GDIOBJ_FreeObj(hObject, GDI_OBJECT_TYPE_DONTCARE) : FALSE;
692 }
693
694 /*!
695 * Internal function. Called when the process is destroyed to free the remaining GDI handles.
696 * \param Process - PID of the process that will be destroyed.
697 */
698 BOOL INTERNAL_CALL
699 GDI_CleanupForProcess (struct _EPROCESS *Process)
700 {
701 PGDI_TABLE_ENTRY Entry, End;
702 PEPROCESS CurrentProcess;
703 PW32PROCESS W32Process;
704 LONG ProcId;
705 ULONG Index = RESERVE_ENTRIES_COUNT;
706
707 DPRINT("Starting CleanupForProcess prochandle %x Pid %d\n", Process, Pid);
708 CurrentProcess = PsGetCurrentProcess();
709 if (CurrentProcess != Process)
710 {
711 KeAttachProcess(Process);
712 }
713 W32Process = Process->Win32Process;
714 ASSERT(W32Process);
715
716 if(W32Process->GDIObjects > 0)
717 {
718 /* FIXME - Instead of building the handle here and delete it using GDIOBJ_FreeObj
719 we should delete it directly here! */
720 ProcId = ((LONG)Process->UniqueProcessId << 1);
721
722 End = &HandleTable->Entries[GDI_HANDLE_COUNT];
723 for(Entry = &HandleTable->Entries[RESERVE_ENTRIES_COUNT];
724 Entry < End;
725 Entry++, Index++)
726 {
727 /* ignore the lock bit */
728 if((Entry->ProcessId & ~0x1) == ProcId && Entry->Type != 0)
729 {
730 HGDIOBJ ObjectHandle;
731
732 /* Create the object handle for the entry, the upper 16 bit of the
733 Type field includes the type of the object including the stock
734 object flag - but since stock objects don't have a process id we can
735 simply ignore this fact here. */
736 ObjectHandle = (HGDIOBJ)(Index | (Entry->Type & 0xFFFF0000));
737
738 if(GDIOBJ_FreeObj(ObjectHandle, GDI_OBJECT_TYPE_DONTCARE) &&
739 W32Process->GDIObjects == 0)
740 {
741 /* there are no more gdi handles for this process, bail */
742 break;
743 }
744 }
745 }
746 }
747
748 if (CurrentProcess != Process)
749 {
750 KeDetachProcess();
751 }
752
753 DPRINT("Completed cleanup for process %d\n", Pid);
754
755 return TRUE;
756 }
757
758 /*!
759 * Return pointer to the object by handle.
760 *
761 * \param hObj Object handle
762 * \return Pointer to the object.
763 *
764 * \note Process can only get pointer to the objects it created or global objects.
765 *
766 * \todo Get rid of the ObjectType parameter!
767 */
768 PGDIOBJ INTERNAL_CALL
769 #ifdef GDI_DEBUG
770 GDIOBJ_LockObjDbg (const char* file, int line, HGDIOBJ hObj, DWORD ObjectType)
771 #else /* !GDI_DEBUG */
772 GDIOBJ_LockObj (HGDIOBJ hObj, DWORD ObjectType)
773 #endif /* GDI_DEBUG */
774 {
775 PGDI_TABLE_ENTRY Entry;
776 PETHREAD Thread;
777 LONG ProcessId, LockedProcessId, PrevProcId, ExpectedType;
778 #ifdef GDI_DEBUG
779 ULONG Attempts = 0;
780 #endif
781
782 DPRINT("GDIOBJ_LockObj: hObj: 0x%08x\n", hObj);
783
784 Thread = PsGetCurrentThread();
785
786 /* shift the process id to the left so we can use the first bit to lock the object.
787 FIXME - don't shift once ROS' PIDs match with nt! */
788 ProcessId = (LONG)PsGetCurrentProcessId() << 1;
789 LockedProcessId = ProcessId | 0x1;
790
791 ExpectedType = ((ObjectType != GDI_OBJECT_TYPE_DONTCARE) ? ObjectType : 0);
792
793 Entry = GDI_HANDLE_GET_ENTRY(HandleTable, hObj);
794
795 LockHandle:
796 /* lock the object, we must not delete stock objects, so don't check!!! */
797 PrevProcId = InterlockedCompareExchange(&Entry->ProcessId, LockedProcessId, ProcessId);
798 if(PrevProcId == ProcessId)
799 {
800 LONG EntryType = Entry->Type << 16;
801
802 /* we're locking an object that belongs to our process or it's a global
803 object if ProcessId == 0 here. ProcessId can only be 0 here if it previously
804 failed to lock the object and it turned out to be a global object. */
805 if(EntryType != 0 && Entry->KernelData != NULL && (ExpectedType == 0 || (EntryType == ExpectedType)))
806 {
807 PETHREAD PrevThread;
808 PGDIOBJHDR GdiHdr;
809
810 GdiHdr = GDIBdyToHdr(Entry->KernelData);
811
812 /* save the pointer to the calling thread so we know it was this thread
813 that locked the object. There's no need to do this atomically as we're
814 holding the lock of the handle slot, but this way it's easier ;) */
815 PrevThread = InterlockedCompareExchangePointer(&GdiHdr->LockingThread, Thread, NULL);
816
817 if(PrevThread == NULL || PrevThread == Thread)
818 {
819 if(++GdiHdr->Locks == 1)
820 {
821 #ifdef GDI_DEBUG
822 GdiHdr->lockfile = file;
823 GdiHdr->lockline = line;
824 #endif
825 }
826
827 InterlockedExchange(&Entry->ProcessId, PrevProcId);
828
829 /* we're done, return the object body */
830 return GDIHdrToBdy(GdiHdr);
831 }
832 else
833 {
834 InterlockedExchange(&Entry->ProcessId, PrevProcId);
835
836 #ifdef GDI_DEBUG
837 if(++Attempts > 20)
838 {
839 DPRINT1("[%d]Waiting at %s:%i as 0x%x on 0x%x\n", Attempts, file, line, Thread, PrevThread);
840 }
841 #endif
842
843 DelayExecution();
844 goto LockHandle;
845 }
846 }
847 else
848 {
849 InterlockedExchange(&Entry->ProcessId, PrevProcId);
850
851 if(EntryType == 0)
852 {
853 DPRINT1("Attempted to lock object 0x%x that is deleted!\n", hObj);
854 KeRosDumpStackFrames ( NULL, 20 );
855 }
856 else
857 {
858 DPRINT1("Attempted to lock object 0x%x, type mismatch (0x%x : 0x%x)\n", hObj, EntryType, ExpectedType);
859 KeRosDumpStackFrames ( NULL, 20 );
860 }
861 #ifdef GDI_DEBUG
862 DPRINT1("-> called from %s:%i\n", file, line);
863 #endif
864 }
865 }
866 else if(PrevProcId == LockedProcessId)
867 {
868 #ifdef GDI_DEBUG
869 if(++Attempts > 20)
870 {
871 DPRINT1("[%d]Waiting from %s:%i on 0x%x\n", Attempts, file, line, hObj);
872 }
873 #endif
874 /* the handle is currently locked, wait some time and try again.
875 FIXME - we shouldn't loop forever! Give up after some time! */
876 DelayExecution();
877 /* try again */
878 goto LockHandle;
879 }
880 else if((PrevProcId & ~0x1) == 0)
881 {
882 /* we're trying to lock a global object, change the ProcessId to 0 and try again */
883 ProcessId = 0x0;
884 LockedProcessId = ProcessId |0x1;
885
886 goto LockHandle;
887 }
888 else
889 {
890 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));
891 KeRosDumpStackFrames ( NULL, 20 );
892 #ifdef GDI_DEBUG
893 DPRINT1("-> called from %s:%i\n", file, line);
894 #endif
895 }
896
897 return NULL;
898 }
899
900
901 /*!
902 * Release GDI object. Every object locked by GDIOBJ_LockObj() must be unlocked. You should unlock the object
903 * as soon as you don't need to have access to it's data.
904
905 * \param hObj Object handle
906 *
907 * \note This function performs delayed cleanup. If the object is locked when GDI_FreeObj() is called
908 * then \em this function frees the object when reference count is zero.
909 */
910 BOOL INTERNAL_CALL
911 #ifdef GDI_DEBUG
912 GDIOBJ_UnlockObjDbg (const char* file, int line, HGDIOBJ hObj)
913 #else /* !GDI_DEBUG */
914 GDIOBJ_UnlockObj (HGDIOBJ hObj)
915 #endif /* GDI_DEBUG */
916 {
917 PGDI_TABLE_ENTRY Entry;
918 PETHREAD Thread;
919 LONG ProcessId, LockedProcessId, PrevProcId;
920 #ifdef GDI_DEBUG
921 ULONG Attempts = 0;
922 #endif
923
924 DPRINT("GDIOBJ_UnlockObj: hObj: 0x%08x\n", hObj);
925 Thread = PsGetCurrentThread();
926
927 /* shift the process id to the left so we can use the first bit to lock the object.
928 FIXME - don't shift once ROS' PIDs match with nt! */
929 ProcessId = (LONG)PsGetCurrentProcessId() << 1;
930 LockedProcessId = ProcessId | 0x1;
931
932 Entry = GDI_HANDLE_GET_ENTRY(HandleTable, hObj);
933
934 LockHandle:
935 /* lock the handle, we must not delete stock objects, so don't check!!! */
936 PrevProcId = InterlockedCompareExchange(&Entry->ProcessId, LockedProcessId, ProcessId);
937 if(PrevProcId == ProcessId)
938 {
939 /* we're unlocking an object that belongs to our process or it's a global
940 object if ProcessId == 0 here. ProcessId can only be 0 here if it previously
941 failed to lock the object and it turned out to be a global object. */
942 if(Entry->KernelData != NULL)
943 {
944 PETHREAD PrevThread;
945 PGDIOBJHDR GdiHdr;
946
947 GdiHdr = GDIBdyToHdr(Entry->KernelData);
948 //ExAllocatePool ( PagedPool, (ULONG)GdiHdr ); // initiate red-zone validation on this block
949
950 PrevThread = GdiHdr->LockingThread;
951 if(PrevThread == Thread)
952 {
953 BOOL Ret;
954
955 if(--GdiHdr->Locks == 0)
956 {
957 GdiHdr->LockingThread = NULL;
958
959 #ifdef GDI_DEBUG
960 GdiHdr->lockfile = NULL;
961 GdiHdr->lockline = 0;
962 #endif
963 }
964
965 if(Entry->Type == 0 && GdiHdr->Locks == 0)
966 {
967 PPAGED_LOOKASIDE_LIST LookasideList;
968 PW32PROCESS W32Process = PsGetWin32Process();
969 DWORD Type = GDI_HANDLE_GET_TYPE(hObj);
970
971 ASSERT(ProcessId != 0); /* must not delete a global handle!!!! */
972
973 /* we should delete the handle */
974 Entry->KernelData = NULL;
975 InterlockedExchange(&Entry->ProcessId, 0);
976
977 InterlockedPushEntrySList(&HandleTable->FreeEntriesHead,
978 &HandleTable->FreeEntries[GDI_ENTRY_TO_INDEX(HandleTable, Entry)]);
979
980 if(W32Process != NULL)
981 {
982 InterlockedDecrement(&W32Process->GDIObjects);
983 }
984
985 /* call the cleanup routine. */
986 Ret = RunCleanupCallback(GDIHdrToBdy(GdiHdr), Type);
987
988 /* Now it's time to free the memory */
989 LookasideList = FindLookasideList(Type);
990 if(LookasideList != NULL)
991 {
992 ExFreeToPagedLookasideList(LookasideList, GdiHdr);
993 }
994 }
995 else
996 {
997 /* remove the handle slot lock */
998 InterlockedExchange(&Entry->ProcessId, PrevProcId);
999 Ret = TRUE;
1000 }
1001
1002 /* we're done*/
1003 return Ret;
1004 }
1005 #ifdef GDI_DEBUG
1006 else if(PrevThread != NULL)
1007 {
1008 DPRINT1("Attempted to unlock object 0x%x, previously locked by other thread (0x%x) from %s:%i (called from %s:%i)\n",
1009 hObj, PrevThread, GdiHdr->lockfile, GdiHdr->lockline, file, line);
1010 InterlockedExchange(&Entry->ProcessId, PrevProcId);
1011 }
1012 #endif
1013 else
1014 {
1015 #ifdef GDI_DEBUG
1016 if(++Attempts > 20)
1017 {
1018 DPRINT1("[%d]Waiting at %s:%i as 0x%x on 0x%x\n", Attempts, file, line, Thread, PrevThread);
1019 }
1020 #endif
1021 /* FIXME - we should give up after some time unless we want to wait forever! */
1022 InterlockedExchange(&Entry->ProcessId, PrevProcId);
1023
1024 DelayExecution();
1025 goto LockHandle;
1026 }
1027 }
1028 else
1029 {
1030 InterlockedExchange(&Entry->ProcessId, PrevProcId);
1031 DPRINT1("Attempted to unlock object 0x%x that is deleted!\n", hObj);
1032 }
1033 }
1034 else if(PrevProcId == LockedProcessId)
1035 {
1036 #ifdef GDI_DEBUG
1037 if(++Attempts > 20)
1038 {
1039 DPRINT1("[%d]Waiting from %s:%i on 0x%x\n", Attempts, file, line, hObj);
1040 }
1041 #endif
1042 /* the handle is currently locked, wait some time and try again.
1043 FIXME - we shouldn't loop forever! Give up after some time! */
1044 DelayExecution();
1045 /* try again */
1046 goto LockHandle;
1047 }
1048 else if((PrevProcId & ~0x1) == 0)
1049 {
1050 /* we're trying to unlock a global object, change the ProcessId to 0 and try again */
1051 ProcessId = 0x0;
1052 LockedProcessId = ProcessId |0x1;
1053
1054 goto LockHandle;
1055 }
1056 else
1057 {
1058 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));
1059 }
1060
1061 return FALSE;
1062 }
1063
1064 BOOL INTERNAL_CALL
1065 GDIOBJ_OwnedByCurrentProcess(HGDIOBJ ObjectHandle)
1066 {
1067 PGDI_TABLE_ENTRY Entry;
1068 LONG ProcessId;
1069 BOOL Ret;
1070
1071 DPRINT("GDIOBJ_OwnedByCurrentProcess: ObjectHandle: 0x%08x\n", ObjectHandle);
1072
1073 if(!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle))
1074 {
1075 ProcessId = (LONG)PsGetCurrentProcessId() << 1;
1076
1077 Entry = GDI_HANDLE_GET_ENTRY(HandleTable, ObjectHandle);
1078 Ret = Entry->KernelData != NULL &&
1079 Entry->Type != 0 &&
1080 (Entry->ProcessId & ~0x1) == ProcessId;
1081
1082 return Ret;
1083 }
1084
1085 return FALSE;
1086 }
1087
1088 BOOL INTERNAL_CALL
1089 GDIOBJ_ConvertToStockObj(HGDIOBJ *hObj)
1090 {
1091 /*
1092 * FIXME !!!!! THIS FUNCTION NEEDS TO BE FIXED - IT IS NOT SAFE WHEN OTHER THREADS
1093 * MIGHT ATTEMPT TO LOCK THE OBJECT DURING THIS CALL!!!
1094 */
1095 PGDI_TABLE_ENTRY Entry;
1096 LONG ProcessId, LockedProcessId, PrevProcId;
1097 PETHREAD Thread;
1098 #ifdef GDI_DEBUG
1099 ULONG Attempts = 0;
1100 #endif
1101
1102 ASSERT(hObj);
1103
1104 DPRINT("GDIOBJ_ConvertToStockObj: hObj: 0x%08x\n", *hObj);
1105
1106 Thread = PsGetCurrentThread();
1107
1108 if(!GDI_HANDLE_IS_STOCKOBJ(*hObj))
1109 {
1110 /* shift the process id to the left so we can use the first bit to lock the object.
1111 FIXME - don't shift once ROS' PIDs match with nt! */
1112 ProcessId = (LONG)PsGetCurrentProcessId() << 1;
1113 LockedProcessId = ProcessId | 0x1;
1114
1115 Entry = GDI_HANDLE_GET_ENTRY(HandleTable, *hObj);
1116
1117 LockHandle:
1118 /* lock the object, we must not convert stock objects, so don't check!!! */
1119 PrevProcId = InterlockedCompareExchange(&Entry->ProcessId, LockedProcessId, ProcessId);
1120 if(PrevProcId == ProcessId)
1121 {
1122 LONG NewType, PrevType, OldType;
1123
1124 /* we're locking an object that belongs to our process. First calculate
1125 the new object type including the stock object flag and then try to
1126 exchange it.*/
1127 NewType = GDI_HANDLE_GET_TYPE(*hObj);
1128 NewType |= NewType >> 16;
1129 /* This is the type that the object should have right now, save it */
1130 OldType = NewType;
1131 /* As the object should be a stock object, set it's flag, but only in the upper 16 bits */
1132 NewType |= GDI_HANDLE_STOCK_MASK;
1133
1134 /* Try to exchange the type field - but only if the old (previous type) matches! */
1135 PrevType = InterlockedCompareExchange(&Entry->Type, NewType, OldType);
1136 if(PrevType == OldType && Entry->KernelData != NULL)
1137 {
1138 PETHREAD PrevThread;
1139 PGDIOBJHDR GdiHdr;
1140
1141 /* We successfully set the stock object flag.
1142 KernelData should never be NULL here!!! */
1143 ASSERT(Entry->KernelData);
1144
1145 GdiHdr = GDIBdyToHdr(Entry->KernelData);
1146
1147 PrevThread = GdiHdr->LockingThread;
1148 if(PrevThread == NULL || PrevThread == Thread)
1149 {
1150 /* dereference the process' object counter */
1151 if(PrevProcId != GDI_GLOBAL_PROCESS)
1152 {
1153 PEPROCESS OldProcess;
1154 PW32PROCESS W32Process;
1155 NTSTATUS Status;
1156
1157 /* FIXME */
1158 Status = PsLookupProcessByProcessId((PVOID)(PrevProcId >> 1), &OldProcess);
1159 if(NT_SUCCESS(Status))
1160 {
1161 W32Process = OldProcess->Win32Process;
1162 if(W32Process != NULL)
1163 {
1164 InterlockedDecrement(&W32Process->GDIObjects);
1165 }
1166 ObDereferenceObject(OldProcess);
1167 }
1168 }
1169
1170 /* remove the process id lock and make it global */
1171 InterlockedExchange(&Entry->ProcessId, GDI_GLOBAL_PROCESS);
1172
1173 *hObj = (HGDIOBJ)((ULONG)(*hObj) | GDI_HANDLE_STOCK_MASK);
1174
1175 /* we're done, successfully converted the object */
1176 return TRUE;
1177 }
1178 else
1179 {
1180 #ifdef GDI_DEBUG
1181 if(++Attempts > 20)
1182 {
1183 if(GdiHdr->lockfile != NULL)
1184 {
1185 DPRINT1("[%d]Locked %s:%i by 0x%x (we're 0x%x)\n", Attempts, GdiHdr->lockfile, GdiHdr->lockline, PrevThread, Thread);
1186 }
1187 }
1188 #endif
1189 /* WTF?! The object is already locked by a different thread!
1190 Release the lock, wait a bit and try again!
1191 FIXME - we should give up after some time unless we want to wait forever! */
1192 InterlockedExchange(&Entry->ProcessId, PrevProcId);
1193
1194 DelayExecution();
1195 goto LockHandle;
1196 }
1197 }
1198 else
1199 {
1200 DPRINT1("Attempted to convert object 0x%x that is deleted! Should never get here!!!\n", hObj);
1201 }
1202 }
1203 else if(PrevProcId == LockedProcessId)
1204 {
1205 #ifdef GDI_DEBUG
1206 if(++Attempts > 20)
1207 {
1208 DPRINT1("[%d]Waiting on 0x%x\n", Attempts, hObj);
1209 }
1210 #endif
1211 /* the object is currently locked, wait some time and try again.
1212 FIXME - we shouldn't loop forever! Give up after some time! */
1213 DelayExecution();
1214 /* try again */
1215 goto LockHandle;
1216 }
1217 else
1218 {
1219 DPRINT1("Attempted to convert invalid handle: 0x%x\n", hObj);
1220 }
1221 }
1222
1223 return FALSE;
1224 }
1225
1226 void INTERNAL_CALL
1227 GDIOBJ_SetOwnership(HGDIOBJ ObjectHandle, PEPROCESS NewOwner)
1228 {
1229 PGDI_TABLE_ENTRY Entry;
1230 LONG ProcessId, LockedProcessId, PrevProcId;
1231 PETHREAD Thread;
1232 #ifdef GDI_DEBUG
1233 ULONG Attempts = 0;
1234 #endif
1235
1236 DPRINT("GDIOBJ_SetOwnership: hObj: 0x%x, NewProcess: 0x%x\n", ObjectHandle, (NewOwner ? PsGetProcessId(NewOwner) : 0));
1237
1238 Thread = PsGetCurrentThread();
1239
1240 if(!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle))
1241 {
1242 /* shift the process id to the left so we can use the first bit to lock the object.
1243 FIXME - don't shift once ROS' PIDs match with nt! */
1244 ProcessId = (LONG)PsGetCurrentProcessId() << 1;
1245 LockedProcessId = ProcessId | 0x1;
1246
1247 Entry = GDI_HANDLE_GET_ENTRY(HandleTable, ObjectHandle);
1248
1249 LockHandle:
1250 /* lock the object, we must not convert stock objects, so don't check!!! */
1251 PrevProcId = InterlockedCompareExchange(&Entry->ProcessId, ProcessId, LockedProcessId);
1252 if(PrevProcId == ProcessId)
1253 {
1254 PETHREAD PrevThread;
1255
1256 if(Entry->Type != 0 && Entry->KernelData != NULL)
1257 {
1258 PGDIOBJHDR GdiHdr = GDIBdyToHdr(Entry->KernelData);
1259
1260 PrevThread = GdiHdr->LockingThread;
1261 if(PrevThread == NULL || PrevThread == Thread)
1262 {
1263 PEPROCESS OldProcess;
1264 PW32PROCESS W32Process;
1265 NTSTATUS Status;
1266
1267 /* dereference the process' object counter */
1268 /* FIXME */
1269 Status = PsLookupProcessByProcessId((PVOID)(PrevProcId >> 1), &OldProcess);
1270 if(NT_SUCCESS(Status))
1271 {
1272 W32Process = OldProcess->Win32Process;
1273 if(W32Process != NULL)
1274 {
1275 InterlockedDecrement(&W32Process->GDIObjects);
1276 }
1277 ObDereferenceObject(OldProcess);
1278 }
1279
1280 if(NewOwner != NULL)
1281 {
1282 /* FIXME */
1283 ProcessId = (LONG)PsGetProcessId(NewOwner) << 1;
1284
1285 /* Increase the new process' object counter */
1286 W32Process = NewOwner->Win32Process;
1287 if(W32Process != NULL)
1288 {
1289 InterlockedIncrement(&W32Process->GDIObjects);
1290 }
1291 }
1292 else
1293 ProcessId = 0;
1294
1295 /* remove the process id lock and change it to the new process id */
1296 InterlockedExchange(&Entry->ProcessId, ProcessId);
1297
1298 /* we're done! */
1299 return;
1300 }
1301 else
1302 {
1303 #ifdef GDI_DEBUG
1304 if(++Attempts > 20)
1305 {
1306 if(GdiHdr->lockfile != NULL)
1307 {
1308 DPRINT1("[%d]Locked from %s:%i by 0x%x (we're 0x%x)\n", Attempts, GdiHdr->lockfile, GdiHdr->lockline, PrevThread, Thread);
1309 }
1310 }
1311 #endif
1312 /* WTF?! The object is already locked by a different thread!
1313 Release the lock, wait a bit and try again! DO reset the pid lock
1314 so we make sure we don't access invalid memory in case the object is
1315 being deleted in the meantime (because we don't have aquired a reference
1316 at this point).
1317 FIXME - we should give up after some time unless we want to wait forever! */
1318 InterlockedExchange(&Entry->ProcessId, PrevProcId);
1319
1320 DelayExecution();
1321 goto LockHandle;
1322 }
1323 }
1324 else
1325 {
1326 DPRINT1("Attempted to change ownership of an object 0x%x currently being destroyed!!!\n", ObjectHandle);
1327 }
1328 }
1329 else if(PrevProcId == LockedProcessId)
1330 {
1331 #ifdef GDI_DEBUG
1332 if(++Attempts > 20)
1333 {
1334 DPRINT1("[%d]Waiting on 0x%x\n", Attempts, ObjectHandle);
1335 }
1336 #endif
1337 /* the object is currently locked, wait some time and try again.
1338 FIXME - we shouldn't loop forever! Give up after some time! */
1339 DelayExecution();
1340 /* try again */
1341 goto LockHandle;
1342 }
1343 else if((PrevProcId >> 1) == 0)
1344 {
1345 /* allow changing ownership of global objects */
1346 ProcessId = 0;
1347 LockedProcessId = ProcessId | 0x1;
1348 goto LockHandle;
1349 }
1350 else if((PrevProcId >> 1) != (LONG)PsGetCurrentProcessId())
1351 {
1352 DPRINT1("Attempted to change ownership of object 0x%x (pid: 0x%x) from pid 0x%x!!!\n", ObjectHandle, PrevProcId >> 1, PsGetCurrentProcessId());
1353 }
1354 else
1355 {
1356 DPRINT1("Attempted to change owner of invalid handle: 0x%x\n", ObjectHandle);
1357 }
1358 }
1359 }
1360
1361 void INTERNAL_CALL
1362 GDIOBJ_CopyOwnership(HGDIOBJ CopyFrom, HGDIOBJ CopyTo)
1363 {
1364 PGDI_TABLE_ENTRY FromEntry;
1365 PETHREAD Thread;
1366 LONG FromProcessId, FromLockedProcessId, FromPrevProcId;
1367 #ifdef GDI_DEBUG
1368 ULONG Attempts = 0;
1369 #endif
1370
1371 DPRINT("GDIOBJ_CopyOwnership: from: 0x%x, to: 0x%x\n", CopyFrom, CopyTo);
1372
1373 Thread = PsGetCurrentThread();
1374
1375 if(!GDI_HANDLE_IS_STOCKOBJ(CopyFrom) && !GDI_HANDLE_IS_STOCKOBJ(CopyTo))
1376 {
1377 FromEntry = GDI_HANDLE_GET_ENTRY(HandleTable, CopyFrom);
1378
1379 FromProcessId = FromEntry->ProcessId & ~0x1;
1380 FromLockedProcessId = FromProcessId | 0x1;
1381
1382 LockHandleFrom:
1383 /* lock the object, we must not convert stock objects, so don't check!!! */
1384 FromPrevProcId = InterlockedCompareExchange(&FromEntry->ProcessId, FromProcessId, FromLockedProcessId);
1385 if(FromPrevProcId == FromProcessId)
1386 {
1387 PETHREAD PrevThread;
1388 PGDIOBJHDR GdiHdr;
1389
1390 if(FromEntry->Type != 0 && FromEntry->KernelData != NULL)
1391 {
1392 GdiHdr = GDIBdyToHdr(FromEntry->KernelData);
1393
1394 /* save the pointer to the calling thread so we know it was this thread
1395 that locked the object */
1396 PrevThread = GdiHdr->LockingThread;
1397 if(PrevThread == NULL || PrevThread == Thread)
1398 {
1399 /* now let's change the ownership of the target object */
1400
1401 if((FromPrevProcId & ~0x1) != 0)
1402 {
1403 PEPROCESS ProcessTo;
1404 /* FIXME */
1405 if(NT_SUCCESS(PsLookupProcessByProcessId((PVOID)(FromPrevProcId >> 1), &ProcessTo)))
1406 {
1407 GDIOBJ_SetOwnership(CopyTo, ProcessTo);
1408 ObDereferenceObject(ProcessTo);
1409 }
1410 }
1411 else
1412 {
1413 /* mark the object as global */
1414 GDIOBJ_SetOwnership(CopyTo, NULL);
1415 }
1416
1417 InterlockedExchange(&FromEntry->ProcessId, FromPrevProcId);
1418 }
1419 else
1420 {
1421 #ifdef GDI_DEBUG
1422 if(++Attempts > 20)
1423 {
1424 if(GdiHdr->lockfile != NULL)
1425 {
1426 DPRINT1("[%d]Locked from %s:%i by 0x%x (we're 0x%x)\n", Attempts, GdiHdr->lockfile, GdiHdr->lockline, PrevThread, Thread);
1427 }
1428 }
1429 #endif
1430 /* WTF?! The object is already locked by a different thread!
1431 Release the lock, wait a bit and try again! DO reset the pid lock
1432 so we make sure we don't access invalid memory in case the object is
1433 being deleted in the meantime (because we don't have aquired a reference
1434 at this point).
1435 FIXME - we should give up after some time unless we want to wait forever! */
1436 InterlockedExchange(&FromEntry->ProcessId, FromPrevProcId);
1437
1438 DelayExecution();
1439 goto LockHandleFrom;
1440 }
1441 }
1442 else
1443 {
1444 DPRINT1("Attempted to copy ownership from an object 0x%x currently being destroyed!!!\n", CopyFrom);
1445 }
1446 }
1447 else if(FromPrevProcId == FromLockedProcessId)
1448 {
1449 #ifdef GDI_DEBUG
1450 if(++Attempts > 20)
1451 {
1452 DPRINT1("[%d]Waiting on 0x%x\n", Attempts, CopyFrom);
1453 }
1454 #endif
1455 /* the object is currently locked, wait some time and try again.
1456 FIXME - we shouldn't loop forever! Give up after some time! */
1457 DelayExecution();
1458 /* try again */
1459 goto LockHandleFrom;
1460 }
1461 else if((FromPrevProcId >> 1) != (LONG)PsGetCurrentProcessId())
1462 {
1463 /* FIXME - should we really allow copying ownership from objects that we don't even own? */
1464 DPRINT1("WARNING! Changing copying ownership of object 0x%x (pid: 0x%x) to pid 0x%x!!!\n", CopyFrom, FromPrevProcId >> 1, PsGetCurrentProcessId());
1465 FromProcessId = FromPrevProcId & ~0x1;
1466 FromLockedProcessId = FromProcessId | 0x1;
1467 goto LockHandleFrom;
1468 }
1469 else
1470 {
1471 DPRINT1("Attempted to copy ownership from invalid handle: 0x%x\n", CopyFrom);
1472 }
1473 }
1474 }
1475
1476 PVOID INTERNAL_CALL
1477 GDI_MapHandleTable(HANDLE hProcess)
1478 {
1479 DPRINT("%s:%i: %s(): FIXME - Map handle table into the process memory space!\n",
1480 __FILE__, __LINE__, __FUNCTION__);
1481 /* FIXME - Map the entire gdi handle table read-only to userland into the
1482 scope of hProcess and return the pointer */
1483 return NULL;
1484 }
1485
1486 /* EOF */