fix jim's copypasta
[reactos.git] / reactos / subsystems / win32 / win32k / ntuser / object.c
1 /*
2 * Server-side USER handles
3 *
4 * Copyright (C) 2001 Alexandre Julliard
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21
22
23 /* INCLUDES ******************************************************************/
24
25 #include <w32k.h>
26
27 #define NDEBUG
28 #include <debug.h>
29
30 int usedHandles=0;
31 PUSER_HANDLE_TABLE gHandleTable = NULL;
32
33
34 PUSER_HANDLE_ENTRY handle_to_entry(PUSER_HANDLE_TABLE ht, HANDLE handle )
35 {
36 unsigned short generation;
37 int index = (((unsigned int)handle & 0xffff) - FIRST_USER_HANDLE) >> 1;
38 if (index < 0 || index >= ht->nb_handles)
39 return NULL;
40 if (!ht->handles[index].type)
41 return NULL;
42 generation = (unsigned int)handle >> 16;
43 if (generation == ht->handles[index].generation || !generation || generation == 0xffff)
44 return &ht->handles[index];
45 return NULL;
46 }
47
48 __inline static HANDLE entry_to_handle(PUSER_HANDLE_TABLE ht, PUSER_HANDLE_ENTRY ptr )
49 {
50 int index = ptr - ht->handles;
51 return (HANDLE)(((index << 1) + FIRST_USER_HANDLE) + (ptr->generation << 16));
52 }
53
54 __inline static PUSER_HANDLE_ENTRY alloc_user_entry(PUSER_HANDLE_TABLE ht)
55 {
56 PUSER_HANDLE_ENTRY entry;
57
58 DPRINT("handles used %i\n",usedHandles);
59
60 if (ht->freelist)
61 {
62 entry = ht->freelist;
63 ht->freelist = entry->ptr;
64
65 usedHandles++;
66 return entry;
67 }
68
69 if (ht->nb_handles >= ht->allocated_handles) /* need to grow the array */
70 {
71 /**/
72 int i, iFree = 0, iWindow = 0, iMenu = 0, iCursorIcon = 0,
73 iHook = 0, iCallProc = 0, iAccel = 0, iMonitor = 0, iTimer = 0;
74 /**/
75 DPRINT1("Out of user handles! Used -> %i, NM_Handle -> %d\n", usedHandles, ht->nb_handles);
76 //#if 0
77 for(i = 0; i < ht->nb_handles; i++)
78 {
79 switch (ht->handles[i].type)
80 {
81 case otFree: // Should be zero.
82 iFree++;
83 break;
84 case otWindow:
85 iWindow++;
86 break;
87 case otMenu:
88 iMenu++;
89 break;
90 case otCursorIcon:
91 iCursorIcon++;
92 break;
93 case otHook:
94 iHook++;
95 break;
96 case otCallProc:
97 iCallProc++;
98 break;
99 case otAccel:
100 iAccel++;
101 break;
102 case otMonitor:
103 iMonitor++;
104 break;
105 case otTimer:
106 iTimer++;
107 break;
108 default:
109 break;
110 }
111 }
112 DPRINT1("Handle Count by Type:\n Free = %d Window = %d Menu = %d CursorIcon = %d Hook = %d\n CallProc = %d Accel = %d Monitor = %d Timer = %d\n",
113 iFree, iWindow, iMenu, iCursorIcon, iHook, iCallProc, iAccel, iMonitor, iTimer );
114 //#endif
115 return NULL;
116 #if 0
117 PUSER_HANDLE_ENTRY new_handles;
118 /* grow array by 50% (but at minimum 32 entries) */
119 int growth = max( 32, ht->allocated_handles / 2 );
120 int new_size = min( ht->allocated_handles + growth, (LAST_USER_HANDLE-FIRST_USER_HANDLE+1) >> 1 );
121 if (new_size <= ht->allocated_handles)
122 return NULL;
123 if (!(new_handles = UserHeapReAlloc( ht->handles, new_size * sizeof(*ht->handles) )))
124 return NULL;
125 ht->handles = new_handles;
126 ht->allocated_handles = new_size;
127 #endif
128 }
129
130 entry = &ht->handles[ht->nb_handles++];
131
132 entry->generation = 1;
133
134 usedHandles++;
135
136 return entry;
137 }
138
139 VOID UserInitHandleTable(PUSER_HANDLE_TABLE ht, PVOID mem, ULONG bytes)
140 {
141 ht->freelist = NULL;
142 ht->handles = mem;
143
144 ht->nb_handles = 0;
145 ht->allocated_handles = bytes / sizeof(USER_HANDLE_ENTRY);
146 }
147
148 __inline static void *free_user_entry(PUSER_HANDLE_TABLE ht, PUSER_HANDLE_ENTRY entry)
149 {
150 void *ret;
151 ret = entry->ptr;
152 entry->ptr = ht->freelist;
153 entry->type = 0;
154 entry->pi = NULL;
155 ht->freelist = entry;
156
157 usedHandles--;
158
159 return ret;
160 }
161
162 static __inline PVOID
163 UserHandleOwnerByType(USER_OBJECT_TYPE type)
164 {
165 PVOID pi;
166
167 switch (type)
168 {
169 case otWindow:
170 pi = GetW32ThreadInfo();
171 break;
172
173 case otMenu:
174 case otCursorIcon:
175 case otHook:
176 case otCallProc:
177 case otAccel:
178 pi = GetW32ProcessInfo();
179 break;
180
181 case otMonitor:
182 pi = NULL; /* System */
183 break;
184
185 default:
186 pi = NULL;
187 break;
188 }
189
190 return pi;
191 }
192
193 /* allocate a user handle for a given object */
194 HANDLE UserAllocHandle(PUSER_HANDLE_TABLE ht, PVOID object, USER_OBJECT_TYPE type )
195 {
196 PUSER_HANDLE_ENTRY entry = alloc_user_entry(ht);
197 if (!entry)
198 return 0;
199 entry->ptr = object;
200 entry->type = type;
201 entry->pi = UserHandleOwnerByType(type);
202 if (++entry->generation >= 0xffff)
203 entry->generation = 1;
204
205 /* We have created a handle, which is a reference! */
206 UserReferenceObject(object);
207
208 return entry_to_handle(ht, entry );
209 }
210
211 /* return a pointer to a user object from its handle */
212 PVOID UserGetObject(PUSER_HANDLE_TABLE ht, HANDLE handle, USER_OBJECT_TYPE type )
213 {
214 PUSER_HANDLE_ENTRY entry;
215
216 ASSERT(ht);
217
218 if (!(entry = handle_to_entry(ht, handle )) || entry->type != type)
219 {
220 SetLastWin32Error(ERROR_INVALID_HANDLE);
221 return NULL;
222 }
223 return entry->ptr;
224 }
225
226
227 /* get the full handle (32bit) for a possibly truncated (16bit) handle */
228 HANDLE get_user_full_handle(PUSER_HANDLE_TABLE ht, HANDLE handle )
229 {
230 PUSER_HANDLE_ENTRY entry;
231
232 if ((unsigned int)handle >> 16)
233 return handle;
234 if (!(entry = handle_to_entry(ht, handle )))
235 return handle;
236 return entry_to_handle( ht, entry );
237 }
238
239
240 /* same as get_user_object plus set the handle to the full 32-bit value */
241 void *get_user_object_handle(PUSER_HANDLE_TABLE ht, HANDLE* handle, USER_OBJECT_TYPE type )
242 {
243 PUSER_HANDLE_ENTRY entry;
244
245 if (!(entry = handle_to_entry(ht, *handle )) || entry->type != type)
246 return NULL;
247 *handle = entry_to_handle( ht, entry );
248 return entry->ptr;
249 }
250
251 /* free a user handle */
252 BOOL UserFreeHandle(PUSER_HANDLE_TABLE ht, HANDLE handle )
253 {
254 PUSER_HANDLE_ENTRY entry;
255 PVOID object;
256
257 if (!(entry = handle_to_entry( ht, handle )))
258 {
259 SetLastNtError( STATUS_INVALID_HANDLE );
260 return FALSE;
261 }
262
263 object = free_user_entry(ht, entry );
264
265 /* We removed the handle, which was a reference! */
266 return UserDereferenceObject(object);
267
268 return TRUE;
269 }
270
271 /* return the next user handle after 'handle' that is of a given type */
272 PVOID UserGetNextHandle(PUSER_HANDLE_TABLE ht, HANDLE* handle, USER_OBJECT_TYPE type )
273 {
274 PUSER_HANDLE_ENTRY entry;
275
276 if (!*handle)
277 entry = ht->handles;
278 else
279 {
280 int index = (((unsigned int)*handle & 0xffff) - FIRST_USER_HANDLE) >> 1;
281 if (index < 0 || index >= ht->nb_handles)
282 return NULL;
283 entry = ht->handles + index + 1; /* start from the next one */
284 }
285 while (entry < ht->handles + ht->nb_handles)
286 {
287 if (!type || entry->type == type)
288 {
289 *handle = entry_to_handle(ht, entry );
290 return entry->ptr;
291 }
292 entry++;
293 }
294 return NULL;
295 }
296
297
298
299 PVOID FASTCALL
300 UserCreateObject(PUSER_HANDLE_TABLE ht, HANDLE* h,USER_OBJECT_TYPE type , ULONG size)
301 {
302
303 HANDLE hi;
304 PUSER_OBJECT_HEADER hdr = UserHeapAlloc(size + sizeof(USER_OBJECT_HEADER));//ExAllocatePool(PagedPool, size + sizeof(USER_OBJECT_HEADER));
305 if (!hdr)
306 return NULL;
307
308
309 hi = UserAllocHandle(ht, USER_HEADER_TO_BODY(hdr), type );
310 if (!hi)
311 {
312 //ExFreePool(hdr);
313 UserHeapFree(hdr);
314 return NULL;
315 }
316
317 RtlZeroMemory(hdr, size + sizeof(USER_OBJECT_HEADER));
318 hdr->hSelf = hi;
319 hdr->RefCount = 2; // we need this, because we create 2 refs: handle and pointer!
320
321 if (h)
322 *h = hi;
323 return USER_HEADER_TO_BODY(hdr);
324 }
325
326 BOOL FASTCALL
327 UserDeleteObject(HANDLE h, USER_OBJECT_TYPE type )
328 {
329 PUSER_OBJECT_HEADER hdr;
330 PVOID body = UserGetObject(gHandleTable, h, type);
331 if (!body)
332 return FALSE;
333
334 hdr = USER_BODY_TO_HEADER(body);
335 ASSERT(hdr->RefCount >= 1);
336
337 hdr->destroyed = TRUE;
338 return UserFreeHandle(gHandleTable, h);
339 }
340
341
342 VOID FASTCALL UserReferenceObject(PVOID obj)
343 {
344 PUSER_OBJECT_HEADER hdr = USER_BODY_TO_HEADER(obj);
345
346 ASSERT(hdr->RefCount >= 0);
347
348 hdr->RefCount++;
349 }
350
351
352 PVOID FASTCALL UserReferenceObjectByHandle(HANDLE handle, USER_OBJECT_TYPE type)
353 {
354 PVOID object;
355
356 object = UserGetObject(gHandleTable, handle, type);
357 if(object)
358 {
359 UserReferenceObject(object);
360 }
361
362 return object;
363 }
364
365
366 HANDLE FASTCALL UserObjectToHandle(PVOID obj)
367 {
368 PUSER_OBJECT_HEADER hdr = USER_BODY_TO_HEADER(obj);
369 return hdr->hSelf;
370 }
371
372
373 BOOL FASTCALL UserDereferenceObject(PVOID obj)
374 {
375 PUSER_OBJECT_HEADER hdr = USER_BODY_TO_HEADER(obj);
376
377 ASSERT(hdr->RefCount >= 1);
378
379 hdr->RefCount--;
380
381 // You can not have a zero here!
382 if (!hdr->destroyed && hdr->RefCount == 0)
383 {
384 hdr->RefCount++; // BOUNCE!!!!!
385 DPRINT1("warning! Dereference to zero without deleting!\n");
386 }
387
388 if (hdr->RefCount == 0 && hdr->destroyed)
389 {
390 // DPRINT1("info: something destroyed bcaise of deref, in use=%i\n",usedHandles);
391
392 memset(hdr, 0x55, sizeof(USER_OBJECT_HEADER));
393
394 return UserHeapFree(hdr);
395 //ExFreePool(hdr);
396
397 return TRUE;
398 }
399
400 return FALSE;
401 }
402
403
404
405 BOOL FASTCALL UserCreateHandleTable()
406 {
407
408 PVOID mem;
409
410 //FIXME: dont alloc all at once! must be mapped into umode also...
411 //mem = ExAllocatePool(PagedPool, sizeof(USER_HANDLE_ENTRY) * 1024*2);
412 mem = UserHeapAlloc(sizeof(USER_HANDLE_ENTRY) * 1024*2);
413 if (!mem)
414 {
415 DPRINT1("Failed creating handle table\n");
416 return FALSE;
417 }
418
419 gHandleTable = UserHeapAlloc(sizeof(USER_HANDLE_TABLE));
420 if (gHandleTable == NULL)
421 {
422 UserHeapFree(mem);
423 DPRINT1("Failed creating handle table\n");
424 return FALSE;
425 }
426
427 //FIXME: make auto growable
428 UserInitHandleTable(gHandleTable, mem, sizeof(USER_HANDLE_ENTRY) * 1024*2);
429
430 return TRUE;
431 }