xref: /reactos/dll/win32/ws2help/context.c (revision 1734f297)
1 /*
2  * COPYRIGHT:   See COPYING in the top level directory
3  * PROJECT:     ReactOS WinSock 2 DLL
4  * FILE:        dll/win32/ws2help/context.c
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 */
604