Synchronize with trunk's revision r57599.
[reactos.git] / dll / win32 / ws2help / context.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS WinSock 2 DLL
4 * FILE: include/ws2_32.h
5 * PURPOSE: WinSock 2 DLL header
6 */
7
8 /* INCLUDES ******************************************************************/
9
10 #include "precomp.h"
11
12 /* DATA **********************************************************************/
13
14 CRITICAL_SECTION WshHandleTableLock;
15 HANDLE ghWriterEvent;
16 DWORD gdwSpinCount = 0;
17 DWORD gHandleToIndexMask;
18
19 CONST DWORD SockPrimes[] =
20 {
21 31, 61, 127, 257, 521, 1031, 2053,
22 4099, 8191, 16381, 32749, 65537, 131071, 261983,
23 -1
24 };
25
26 typedef volatile LONG VLONG;
27 typedef VLONG *PVLONG;
28
29 /* DEFINES *******************************************************************/
30
31 /* Yes, we "abuse" the lower bits */
32 #define WSH_SEARCH_TABLE_FROM_HANDLE(h, t) \
33 (&t->SearchTables[(((ULONG_PTR)h >> 2) & t->Mask)])
34
35 #define WSH_HASH_FROM_HANDLE(h, hs) \
36 (hs->Handles[((ULONG_PTR)h % hs->Size)])
37
38 #define AcquireWriteLock(t) \
39 EnterCriticalSection(&t->Lock);
40
41 #define ReleaseWriteLock(t) \
42 LeaveCriticalSection(&t->Lock);
43
44 /* FUNCTIONS *****************************************************************/
45
46 VOID
47 static __inline
48 AcquireReadLock(IN PWAH_SEARCH_TABLE Table,
49 IN PVLONG *Count)
50 {
51 LONG OldCount;
52
53 /* Start acquire loop */
54 do
55 {
56 /* Write and save count value */
57 *Count = Table->CurrentCount;
58 OldCount = **Count;
59
60 /* Check if it's valid and try to increment it */
61 if ((OldCount > 0) && (InterlockedCompareExchange(*Count,
62 OldCount + 1,
63 OldCount) == OldCount))
64 {
65 /* Everything went OK */
66 break;
67 }
68 } while (TRUE);
69 }
70
71 VOID
72 static __inline
73 ReleaseReadLock(IN PWAH_SEARCH_TABLE Table,
74 IN PVLONG Count)
75 {
76 /* Decrement the count. If we went below 0, someone is waiting... */
77 if (InterlockedDecrement(Count) < 0)
78 {
79 /* We use pulse since this is a shared event */
80 PulseEvent(ghWriterEvent);
81 }
82 }
83
84 VOID
85 WINAPI
86 DoWaitForReaders(IN PWAH_SEARCH_TABLE Table,
87 IN PVLONG Counter)
88 {
89 HANDLE EventHandle;
90
91 /* Do a context switch */
92 SwitchToThread();
93
94 /* Check if the counter is above one */
95 if (*Counter > 0)
96 {
97 /*
98 * This shouldn't happen unless priorities are messed up. Do a wait so
99 * that the threads with lower priority will get their chance now.
100 */
101 if (!ghWriterEvent)
102 {
103 /* We don't even have an event! Allocate one manually... */
104 EventHandle = CreateEvent(NULL, TRUE, FALSE, NULL);
105 if (EventHandle)
106 {
107 /* Save the event handle atomically */
108 if ((InterlockedCompareExchangePointer((PVOID*)&ghWriterEvent,
109 EventHandle,
110 NULL)))
111 {
112 /* Someone beat us to it, close ours */
113 CloseHandle(EventHandle);
114 }
115 }
116 else
117 {
118 /* Things couldn't get worse for us. Do a last-resort hack */
119 while (*Counter > 0) Sleep(10);
120 }
121 }
122
123 /*
124 * Our event is ready. Tell the others to signal us by making sure
125 * that the last counter will be -1, notifying the last thread of our
126 * request.
127 */
128 if (InterlockedDecrement(Counter) >= 0)
129 {
130 /* Keep looping */
131 do
132 {
133 /* Wait in tiny bursts, so we can catch the PulseEvent */
134 WaitForSingleObject(ghWriterEvent, 10);
135 } while (*Counter >= 0);
136 }
137 }
138 }
139
140 VOID
141 static __inline
142 TryWaitForReaders(IN PWAH_SEARCH_TABLE Table)
143 {
144 PVLONG OldCount = Table->CurrentCount;
145 LONG SpinCount;
146
147 /* See which counter is being used */
148 if (OldCount == &Table->Count1)
149 {
150 /* Use counter 2 now */
151 Table->Count2 = 1;
152 Table->CurrentCount = &Table->Count2;
153 }
154 else
155 {
156 /* Use counter 1 now */
157 Table->Count1 = 1;
158 Table->CurrentCount = &Table->Count1;
159 }
160
161 /* Decrease the old count to block new readers */
162 if (InterlockedDecrement(OldCount) > 0)
163 {
164 /* On an MP machine, spin a bit first */
165 if (Table->SpinCount)
166 {
167 /* Get the spincount and loop it */
168 SpinCount = Table->SpinCount;
169 while (*OldCount > 0)
170 {
171 /* Check if the spin failed */
172 if (--SpinCount <= 0) break;
173 }
174 }
175
176 /* Check one last time if someone is still active */
177 if (*OldCount > 0)
178 {
179 /* Yep, we'll have to do a blocking (slow) wait */
180 DoWaitForReaders(Table, OldCount);
181 }
182 }
183 }
184
185 DWORD
186 WINAPI
187 WahCreateHandleContextTable(OUT PWAH_HANDLE_TABLE *Table)
188 {
189 DWORD ErrorCode;
190 PWAH_HANDLE_TABLE LocalTable;
191 DWORD i;
192
193 /* Enter the prolog, make sure we're initialized */
194 ErrorCode = WS2HELP_PROLOG();
195 if (ErrorCode != ERROR_SUCCESS) return ErrorCode;
196
197 /* Assume NULL */
198 *Table = NULL;
199
200 /* Allocate enough tables */
201 LocalTable = HeapAlloc(GlobalHeap,
202 0,
203 FIELD_OFFSET(WSH_HANDLE_TABLE,
204 SearchTables[gHandleToIndexMask + 1]));
205
206 /* Make sure it was allocated */
207 if (!LocalTable) return WSA_NOT_ENOUGH_MEMORY;
208
209 /* Set the mask for the table */
210 LocalTable->Mask = gHandleToIndexMask;
211
212 /* Now initialize every table */
213 for (i = 0; i <= gHandleToIndexMask; i++)
214 {
215 /* No hash table yet */
216 LocalTable->SearchTables[i].HashTable = NULL;
217
218 /* Set the current count */
219 LocalTable->SearchTables[i].CurrentCount = &LocalTable->SearchTables[i].Count1;
220
221 /* Initialize the counts */
222 LocalTable->SearchTables[i].Count1 = 1;
223 LocalTable->SearchTables[i].Count2 = 0;
224
225 /* Set expanding state and spin count */
226 LocalTable->SearchTables[i].Expanding = FALSE;
227 LocalTable->SearchTables[i].SpinCount = gdwSpinCount;
228
229 /* Initialize the lock */
230 (VOID)InitializeCriticalSectionAndSpinCount(&LocalTable->
231 SearchTables[i].Lock,
232 gdwSpinCount);
233 }
234
235 /* Return pointer */
236 *Table = LocalTable;
237
238 /* Return success */
239 return ERROR_SUCCESS;
240 }
241
242 DWORD
243 WINAPI
244 WahDestroyHandleContextTable(IN PWAH_HANDLE_TABLE Table)
245 {
246 DWORD i;
247
248 /* Make sure the table is valid */
249 if (!Table)
250 {
251 /* No valid table */
252 return ERROR_INVALID_PARAMETER;
253 }
254
255 /* Loop each search table */
256 for (i = 0; i <= Table->Mask; i++)
257 {
258 /* Check if there's a table here */
259 if (Table->SearchTables[i].HashTable)
260 {
261 /* Free it */
262 HeapFree(GlobalHeap, 0, Table->SearchTables[i].HashTable);
263 }
264
265 /* Delete the lock */
266 DeleteCriticalSection(&Table->SearchTables[i].Lock);
267 }
268
269 /* Delete the table */
270 HeapFree(GlobalHeap, 0, Table);
271
272 /* Return success */
273 return ERROR_SUCCESS;
274 }
275
276 BOOL
277 WINAPI
278 WahEnumerateHandleContexts(IN PWAH_HANDLE_TABLE Table,
279 IN PWAH_HANDLE_ENUMERATE_PROC Callback,
280 IN PVOID Context)
281 {
282 DWORD i, j;
283 PWAH_SEARCH_TABLE SearchTable;
284 PWAH_HASH_TABLE HashTable;
285 PWAH_HANDLE Handle;
286 BOOL GoOn = TRUE;
287
288 /* Loop the table */
289 for (i = 0; i <= Table->Mask; i++)
290 {
291 /* Get the Search table */
292 SearchTable = &Table->SearchTables[i];
293
294 /* Lock it */
295 AcquireWriteLock(SearchTable);
296
297 /* Mark us as expanding and wait for everyone */
298 SearchTable->Expanding = TRUE;
299 TryWaitForReaders(SearchTable);
300
301 /* Get the hash table */
302 HashTable = SearchTable->HashTable;
303
304 /* Make sure it exists */
305 if (HashTable)
306 {
307 /* Loop every handle in it */
308 for (j = 0; j < HashTable->Size; j++)
309 {
310 /* Get this handle */
311 Handle = HashTable->Handles[j];
312 if (!Handle) continue;
313
314 /* Call the callback proc */
315 GoOn = Callback(Context, Handle);
316 if (!GoOn) break;
317 }
318 }
319
320 /* Disable the expansion bit and release the lock */
321 SearchTable->Expanding = FALSE;
322 ReleaseWriteLock(SearchTable);
323
324 /* Check again if we should leave */
325 if (!GoOn) break;
326 }
327
328 /* return */
329 return GoOn;
330 }
331
332 PWAH_HANDLE
333 WINAPI
334 WahInsertHandleContext(IN PWAH_HANDLE_TABLE Table,
335 IN PWAH_HANDLE Handle)
336 {
337 PWAH_HANDLE *HashHandle, OldHandle;
338 PVLONG Count;
339 PWAH_HASH_TABLE HashTable, NewHashTable;
340 DWORD HandleCount, i;
341 PWAH_SEARCH_TABLE SearchTable;
342
343 /* Get the current Search Table */
344 SearchTable = WSH_SEARCH_TABLE_FROM_HANDLE(Handle->Handle, Table);
345
346 /* Start loop */
347 do
348 {
349 /* Get reader lock */
350 AcquireReadLock(SearchTable, &Count);
351
352 /* Get the hash table */
353 HashTable = SearchTable->HashTable;
354
355 /* Make sure we are not expanding, and that the table is there */
356 if (!(SearchTable->Expanding) && (HashTable))
357 {
358 /* Get the hash handle */
359 HashHandle = &WSH_HASH_FROM_HANDLE(Handle->Handle, HashTable);
360
361 /* Do the insert */
362 if (InterlockedCompareExchangePointer((PVOID*)HashHandle,
363 Handle,
364 NULL) == NULL)
365 {
366 /* Success, release the reader lock */
367 ReleaseReadLock(SearchTable, Count);
368
369 /* Save old handle */
370 OldHandle = Handle;
371 break;
372 }
373 }
374
375 /* Release the read lock since we're done with it now */
376 ReleaseReadLock(SearchTable, Count);
377
378 /* We need the writer lock to expand/create the table */
379 AcquireWriteLock(SearchTable);
380
381 /* Mark the table in use */
382 SearchTable->Expanding = TRUE;
383
384 /* Wait for all the readers to finish */
385 TryWaitForReaders(SearchTable);
386
387 /* Start loop */
388 do
389 {
390 /* Get the hash table again */
391 HashTable = SearchTable->HashTable;
392
393 /* Check if exists now */
394 if (HashTable)
395 {
396 /* It does! Do what we wanted to do earlier. Get the hash... */
397 HashHandle = &WSH_HASH_FROM_HANDLE(Handle->Handle, HashTable);
398
399 /* Check if it doesn't exist */
400 if (!(*HashHandle))
401 {
402 /* Write it (no need for interlock, we have the RW lock) */
403 OldHandle = Handle;
404 *HashHandle = Handle;
405 break;
406 }
407 else if ((*HashHandle)->Handle == Handle->Handle)
408 {
409 /* Handle matches, write it (see comment above) */
410 OldHandle = *HashHandle;
411 *HashHandle = Handle;
412 break;
413 }
414
415 /* No go, we need to expand the table. Remember the size now */
416 HandleCount = HashTable->Size;
417 }
418 else
419 {
420 /* Table is empty, we have to create it */
421 HandleCount = 0;
422 }
423
424 ExpandTable:
425 /* Find a free prime */
426 for (i = 0; HandleCount >= SockPrimes[i]; i++);
427
428 /* Check if we found one */
429 if (SockPrimes[i] != 0xFFFFFFFF)
430 {
431 /* Use the prime */
432 HandleCount = SockPrimes[i];
433 }
434 else
435 {
436 /* No primes left. Table is quite large, so simply double it */
437 HandleCount *= 2;
438 }
439
440 /* Allocate the table */
441 NewHashTable = HeapAlloc(GlobalHeap,
442 0,
443 FIELD_OFFSET(WSH_HASH_TABLE,
444 Handles[HandleCount]));
445
446 /* Hopefully we have one now */
447 if (NewHashTable)
448 {
449 /* Set its size */
450 NewHashTable->Size = HandleCount;
451
452 /* Initialize it */
453 RtlZeroMemory(NewHashTable->Handles, HandleCount * sizeof(PVOID));
454
455 /* Insert us first */
456 WSH_HASH_FROM_HANDLE(Handle->Handle, NewHashTable) = Handle;
457
458 /* Now check if our old table had entries in it */
459 if (HashTable)
460 {
461 /* We need to move them */
462 for (i = 0; i < HashTable->Size; i++)
463 {
464 /* Make sure the hash handle exists */
465 if (HashTable->Handles[i])
466 {
467 /* Get it */
468 HashHandle = &WSH_HASH_FROM_HANDLE(HashTable->
469 Handles[i]->Handle,
470 NewHashTable);
471
472 /* Check if it has a value */
473 if (!(*HashHandle))
474 {
475 /* It's empty, so just write the handle */
476 *HashHandle = HashTable->Handles[i];
477 }
478 else
479 {
480 /* Not empty :/... that implies a collision */
481 HeapFree(GlobalHeap, 0, NewHashTable);
482 goto ExpandTable;
483 }
484 }
485 }
486
487 /* Write the new hash table */
488 SearchTable->HashTable = NewHashTable;
489
490 /* Wait for everyone to be done with it, then free it */
491 TryWaitForReaders(SearchTable);
492 HeapFree(GlobalHeap, 0, HashTable);
493 }
494 else
495 {
496 /* It was empty, nothing to worry about */
497 SearchTable->HashTable = NewHashTable;
498 }
499
500 /* Save the old handle */
501 OldHandle = Handle;
502 }
503 else
504 {
505 /* There was no old handle */
506 OldHandle = Handle;
507 }
508 } while (0);
509
510 /* Mark us as free, and release the write lock */
511 SearchTable->Expanding = FALSE;
512 ReleaseWriteLock(SearchTable);
513 break;
514 } while (1);
515
516 /* Return the old handle */
517 return OldHandle;
518 }
519
520 PWAH_HANDLE
521 WINAPI
522 WahReferenceContextByHandle(IN PWAH_HANDLE_TABLE Table,
523 IN HANDLE Handle)
524 {
525 PWAH_HANDLE HashHandle;
526 PWAH_SEARCH_TABLE SearchTable;
527 PWAH_HASH_TABLE HashTable;
528 PVLONG Count;
529
530 /* Get the current Search Table */
531 SearchTable = WSH_SEARCH_TABLE_FROM_HANDLE(Handle, Table);
532
533 /* Lock it */
534 AcquireReadLock(SearchTable, &Count);
535
536 /* Get the hash table and handle */
537 HashTable = SearchTable->HashTable;
538
539 /* Check if it's valid, and if it's the one we want */
540 if ((HashTable) &&
541 (HashHandle = WSH_HASH_FROM_HANDLE(Handle, HashTable)) &&
542 (HashHandle->Handle == Handle))
543 {
544 /* Reference the handle */
545 InterlockedIncrement(&HashHandle->RefCount);
546 }
547 else
548 {
549 /* Invalid handle */
550 HashHandle = NULL;
551 }
552
553 /* Release the lock */
554 ReleaseReadLock(SearchTable, Count);
555
556 /* Return */
557 return HashHandle;
558 }
559
560 DWORD
561 WINAPI
562 WahRemoveHandleContext(IN PWAH_HANDLE_TABLE Table,
563 IN PWAH_HANDLE Handle)
564 {
565 PWAH_HANDLE *HashHandle;
566 PWAH_SEARCH_TABLE SearchTable;
567 PWAH_HASH_TABLE HashTable;
568 DWORD ErrorCode = ERROR_SUCCESS;
569
570 /* Get the current Search Table */
571 SearchTable = WSH_SEARCH_TABLE_FROM_HANDLE(Handle->Handle, Table);
572
573 /* Lock it */
574 AcquireWriteLock(SearchTable);
575
576 /* Get the hash table and handle */
577 HashTable = SearchTable->HashTable;
578 HashHandle = &WSH_HASH_FROM_HANDLE(Handle->Handle, HashTable);
579
580 /* Make sure we have a handle, and write the new pointer */
581 if (HashHandle && (InterlockedCompareExchangePointer((PVOID*)HashHandle,
582 NULL,
583 Handle) == Handle))
584 {
585 /* Wait for everyone to be done with it */
586 TryWaitForReaders(SearchTable);
587 }
588 else
589 {
590 /* Invalid handle */
591 ErrorCode = ERROR_INVALID_PARAMETER;
592 }
593
594 /* Release the lock */
595 ReleaseWriteLock(SearchTable);
596
597 /* Return */
598 return ErrorCode;
599 }
600
601 /* EOF */