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