mirror of
https://github.com/reactos/reactos.git
synced 2024-10-29 19:13:58 +00:00
9393fc320e
Excluded: 3rd-party code (incl. wine) and most of the win32ss.
604 lines
17 KiB
C
604 lines
17 KiB
C
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS WinSock 2 DLL
|
|
* FILE: dll/win32/ws2help/context.c
|
|
* PURPOSE: WinSock 2 DLL header
|
|
*/
|
|
|
|
/* INCLUDES ******************************************************************/
|
|
|
|
#include "precomp.h"
|
|
|
|
#include <ws2help.h>
|
|
|
|
/* DATA **********************************************************************/
|
|
|
|
CRITICAL_SECTION WshHandleTableLock;
|
|
HANDLE ghWriterEvent;
|
|
DWORD gdwSpinCount = 0;
|
|
DWORD gHandleToIndexMask;
|
|
|
|
CONST DWORD SockPrimes[] =
|
|
{
|
|
31, 61, 127, 257, 521, 1031, 2053,
|
|
4099, 8191, 16381, 32749, 65537, 131071, 261983,
|
|
-1
|
|
};
|
|
|
|
typedef volatile LONG VLONG;
|
|
typedef VLONG *PVLONG;
|
|
|
|
/* DEFINES *******************************************************************/
|
|
|
|
/* Yes, we "abuse" the lower bits */
|
|
#define WSH_SEARCH_TABLE_FROM_HANDLE(h, t) \
|
|
(&t->SearchTables[(((ULONG_PTR)h >> 2) & t->Mask)])
|
|
|
|
#define WSH_HASH_FROM_HANDLE(h, hs) \
|
|
(hs->Handles[((ULONG_PTR)h % hs->Size)])
|
|
|
|
#define AcquireWriteLock(t) \
|
|
EnterCriticalSection(&t->Lock);
|
|
|
|
#define ReleaseWriteLock(t) \
|
|
LeaveCriticalSection(&t->Lock);
|
|
|
|
/* FUNCTIONS *****************************************************************/
|
|
|
|
static __inline
|
|
VOID
|
|
AcquireReadLock(IN PWAH_SEARCH_TABLE Table,
|
|
IN PVLONG *Count)
|
|
{
|
|
LONG OldCount;
|
|
|
|
/* Start acquire loop */
|
|
do
|
|
{
|
|
/* Write and save count value */
|
|
*Count = Table->CurrentCount;
|
|
OldCount = **Count;
|
|
|
|
/* Check if it's valid and try to increment it */
|
|
if ((OldCount > 0) && (InterlockedCompareExchange(*Count,
|
|
OldCount + 1,
|
|
OldCount) == OldCount))
|
|
{
|
|
/* Everything went OK */
|
|
break;
|
|
}
|
|
} while (TRUE);
|
|
}
|
|
|
|
static __inline
|
|
VOID
|
|
ReleaseReadLock(IN PWAH_SEARCH_TABLE Table,
|
|
IN PVLONG Count)
|
|
{
|
|
/* Decrement the count. If we went below 0, someone is waiting... */
|
|
if (InterlockedDecrement(Count) < 0)
|
|
{
|
|
/* We use pulse since this is a shared event */
|
|
PulseEvent(ghWriterEvent);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
WINAPI
|
|
DoWaitForReaders(IN PWAH_SEARCH_TABLE Table,
|
|
IN PVLONG Counter)
|
|
{
|
|
HANDLE EventHandle;
|
|
|
|
/* Do a context switch */
|
|
SwitchToThread();
|
|
|
|
/* Check if the counter is above one */
|
|
if (*Counter > 0)
|
|
{
|
|
/*
|
|
* This shouldn't happen unless priorities are messed up. Do a wait so
|
|
* that the threads with lower priority will get their chance now.
|
|
*/
|
|
if (!ghWriterEvent)
|
|
{
|
|
/* We don't even have an event! Allocate one manually... */
|
|
EventHandle = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
if (EventHandle)
|
|
{
|
|
/* Save the event handle atomically */
|
|
if ((InterlockedCompareExchangePointer((PVOID*)&ghWriterEvent,
|
|
EventHandle,
|
|
NULL)))
|
|
{
|
|
/* Someone beat us to it, close ours */
|
|
CloseHandle(EventHandle);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Things couldn't get worse for us. Do a last-resort hack */
|
|
while (*Counter > 0) Sleep(10);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Our event is ready. Tell the others to signal us by making sure
|
|
* that the last counter will be -1, notifying the last thread of our
|
|
* request.
|
|
*/
|
|
if (InterlockedDecrement(Counter) >= 0)
|
|
{
|
|
/* Keep looping */
|
|
do
|
|
{
|
|
/* Wait in tiny bursts, so we can catch the PulseEvent */
|
|
WaitForSingleObject(ghWriterEvent, 10);
|
|
} while (*Counter >= 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
static __inline
|
|
VOID
|
|
TryWaitForReaders(IN PWAH_SEARCH_TABLE Table)
|
|
{
|
|
PVLONG OldCount = Table->CurrentCount;
|
|
LONG SpinCount;
|
|
|
|
/* See which counter is being used */
|
|
if (OldCount == &Table->Count1)
|
|
{
|
|
/* Use counter 2 now */
|
|
Table->Count2 = 1;
|
|
Table->CurrentCount = &Table->Count2;
|
|
}
|
|
else
|
|
{
|
|
/* Use counter 1 now */
|
|
Table->Count1 = 1;
|
|
Table->CurrentCount = &Table->Count1;
|
|
}
|
|
|
|
/* Decrease the old count to block new readers */
|
|
if (InterlockedDecrement(OldCount) > 0)
|
|
{
|
|
/* On an MP machine, spin a bit first */
|
|
if (Table->SpinCount)
|
|
{
|
|
/* Get the spincount and loop it */
|
|
SpinCount = Table->SpinCount;
|
|
while (*OldCount > 0)
|
|
{
|
|
/* Check if the spin failed */
|
|
if (--SpinCount <= 0) break;
|
|
}
|
|
}
|
|
|
|
/* Check one last time if someone is still active */
|
|
if (*OldCount > 0)
|
|
{
|
|
/* Yep, we'll have to do a blocking (slow) wait */
|
|
DoWaitForReaders(Table, OldCount);
|
|
}
|
|
}
|
|
}
|
|
|
|
DWORD
|
|
WINAPI
|
|
WahCreateHandleContextTable(OUT PWAH_HANDLE_TABLE *Table)
|
|
{
|
|
DWORD ErrorCode;
|
|
PWAH_HANDLE_TABLE LocalTable;
|
|
DWORD i;
|
|
|
|
/* Enter the prolog, make sure we're initialized */
|
|
ErrorCode = WS2HELP_PROLOG();
|
|
if (ErrorCode != ERROR_SUCCESS) return ErrorCode;
|
|
|
|
/* Assume NULL */
|
|
*Table = NULL;
|
|
|
|
/* Allocate enough tables */
|
|
LocalTable = HeapAlloc(GlobalHeap,
|
|
0,
|
|
FIELD_OFFSET(WSH_HANDLE_TABLE,
|
|
SearchTables[gHandleToIndexMask + 1]));
|
|
|
|
/* Make sure it was allocated */
|
|
if (!LocalTable) return WSA_NOT_ENOUGH_MEMORY;
|
|
|
|
/* Set the mask for the table */
|
|
LocalTable->Mask = gHandleToIndexMask;
|
|
|
|
/* Now initialize every table */
|
|
for (i = 0; i <= gHandleToIndexMask; i++)
|
|
{
|
|
/* No hash table yet */
|
|
LocalTable->SearchTables[i].HashTable = NULL;
|
|
|
|
/* Set the current count */
|
|
LocalTable->SearchTables[i].CurrentCount = &LocalTable->SearchTables[i].Count1;
|
|
|
|
/* Initialize the counts */
|
|
LocalTable->SearchTables[i].Count1 = 1;
|
|
LocalTable->SearchTables[i].Count2 = 0;
|
|
|
|
/* Set expanding state and spin count */
|
|
LocalTable->SearchTables[i].Expanding = FALSE;
|
|
LocalTable->SearchTables[i].SpinCount = gdwSpinCount;
|
|
|
|
/* Initialize the lock */
|
|
(VOID)InitializeCriticalSectionAndSpinCount(&LocalTable->
|
|
SearchTables[i].Lock,
|
|
gdwSpinCount);
|
|
}
|
|
|
|
/* Return pointer */
|
|
*Table = LocalTable;
|
|
|
|
/* Return success */
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
DWORD
|
|
WINAPI
|
|
WahDestroyHandleContextTable(IN PWAH_HANDLE_TABLE Table)
|
|
{
|
|
DWORD i;
|
|
|
|
/* Make sure the table is valid */
|
|
if (!Table)
|
|
{
|
|
/* No valid table */
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Loop each search table */
|
|
for (i = 0; i <= Table->Mask; i++)
|
|
{
|
|
/* Check if there's a table here */
|
|
if (Table->SearchTables[i].HashTable)
|
|
{
|
|
/* Free it */
|
|
HeapFree(GlobalHeap, 0, Table->SearchTables[i].HashTable);
|
|
}
|
|
|
|
/* Delete the lock */
|
|
DeleteCriticalSection(&Table->SearchTables[i].Lock);
|
|
}
|
|
|
|
/* Delete the table */
|
|
HeapFree(GlobalHeap, 0, Table);
|
|
|
|
/* Return success */
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
BOOL
|
|
WINAPI
|
|
WahEnumerateHandleContexts(IN PWAH_HANDLE_TABLE Table,
|
|
IN PWAH_HANDLE_ENUMERATE_PROC Callback,
|
|
IN PVOID Context)
|
|
{
|
|
DWORD i, j;
|
|
PWAH_SEARCH_TABLE SearchTable;
|
|
PWAH_HASH_TABLE HashTable;
|
|
PWAH_HANDLE Handle;
|
|
BOOL GoOn = TRUE;
|
|
|
|
/* Loop the table */
|
|
for (i = 0; i <= Table->Mask; i++)
|
|
{
|
|
/* Get the Search table */
|
|
SearchTable = &Table->SearchTables[i];
|
|
|
|
/* Lock it */
|
|
AcquireWriteLock(SearchTable);
|
|
|
|
/* Mark us as expanding and wait for everyone */
|
|
SearchTable->Expanding = TRUE;
|
|
TryWaitForReaders(SearchTable);
|
|
|
|
/* Get the hash table */
|
|
HashTable = SearchTable->HashTable;
|
|
|
|
/* Make sure it exists */
|
|
if (HashTable)
|
|
{
|
|
/* Loop every handle in it */
|
|
for (j = 0; j < HashTable->Size; j++)
|
|
{
|
|
/* Get this handle */
|
|
Handle = HashTable->Handles[j];
|
|
if (!Handle) continue;
|
|
|
|
/* Call the callback proc */
|
|
GoOn = Callback(Context, Handle);
|
|
if (!GoOn) break;
|
|
}
|
|
}
|
|
|
|
/* Disable the expansion bit and release the lock */
|
|
SearchTable->Expanding = FALSE;
|
|
ReleaseWriteLock(SearchTable);
|
|
|
|
/* Check again if we should leave */
|
|
if (!GoOn) break;
|
|
}
|
|
|
|
/* return */
|
|
return GoOn;
|
|
}
|
|
|
|
PWAH_HANDLE
|
|
WINAPI
|
|
WahInsertHandleContext(IN PWAH_HANDLE_TABLE Table,
|
|
IN PWAH_HANDLE Handle)
|
|
{
|
|
PWAH_HANDLE *HashHandle, OldHandle;
|
|
PVLONG Count;
|
|
PWAH_HASH_TABLE HashTable, NewHashTable;
|
|
DWORD HandleCount, i;
|
|
PWAH_SEARCH_TABLE SearchTable;
|
|
|
|
/* Get the current Search Table */
|
|
SearchTable = WSH_SEARCH_TABLE_FROM_HANDLE(Handle->Handle, Table);
|
|
|
|
/* Start loop */
|
|
do
|
|
{
|
|
/* Get reader lock */
|
|
AcquireReadLock(SearchTable, &Count);
|
|
|
|
/* Get the hash table */
|
|
HashTable = SearchTable->HashTable;
|
|
|
|
/* Make sure we are not expanding, and that the table is there */
|
|
if (!(SearchTable->Expanding) && (HashTable))
|
|
{
|
|
/* Get the hash handle */
|
|
HashHandle = &WSH_HASH_FROM_HANDLE(Handle->Handle, HashTable);
|
|
|
|
/* Do the insert */
|
|
if (InterlockedCompareExchangePointer((PVOID*)HashHandle,
|
|
Handle,
|
|
NULL) == NULL)
|
|
{
|
|
/* Success, release the reader lock */
|
|
ReleaseReadLock(SearchTable, Count);
|
|
|
|
/* Save old handle */
|
|
OldHandle = Handle;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Release the read lock since we're done with it now */
|
|
ReleaseReadLock(SearchTable, Count);
|
|
|
|
/* We need the writer lock to expand/create the table */
|
|
AcquireWriteLock(SearchTable);
|
|
|
|
/* Mark the table in use */
|
|
SearchTable->Expanding = TRUE;
|
|
|
|
/* Wait for all the readers to finish */
|
|
TryWaitForReaders(SearchTable);
|
|
|
|
/* Start loop */
|
|
do
|
|
{
|
|
/* Get the hash table again */
|
|
HashTable = SearchTable->HashTable;
|
|
|
|
/* Check if exists now */
|
|
if (HashTable)
|
|
{
|
|
/* It does! Do what we wanted to do earlier. Get the hash... */
|
|
HashHandle = &WSH_HASH_FROM_HANDLE(Handle->Handle, HashTable);
|
|
|
|
/* Check if it doesn't exist */
|
|
if (!(*HashHandle))
|
|
{
|
|
/* Write it (no need for interlock, we have the RW lock) */
|
|
OldHandle = Handle;
|
|
*HashHandle = Handle;
|
|
break;
|
|
}
|
|
else if ((*HashHandle)->Handle == Handle->Handle)
|
|
{
|
|
/* Handle matches, write it (see comment above) */
|
|
OldHandle = *HashHandle;
|
|
*HashHandle = Handle;
|
|
break;
|
|
}
|
|
|
|
/* No go, we need to expand the table. Remember the size now */
|
|
HandleCount = HashTable->Size;
|
|
}
|
|
else
|
|
{
|
|
/* Table is empty, we have to create it */
|
|
HandleCount = 0;
|
|
}
|
|
|
|
ExpandTable:
|
|
/* Find a free prime */
|
|
for (i = 0; HandleCount >= SockPrimes[i]; i++);
|
|
|
|
/* Check if we found one */
|
|
if (SockPrimes[i] != 0xFFFFFFFF)
|
|
{
|
|
/* Use the prime */
|
|
HandleCount = SockPrimes[i];
|
|
}
|
|
else
|
|
{
|
|
/* No primes left. Table is quite large, so simply double it */
|
|
HandleCount *= 2;
|
|
}
|
|
|
|
/* Allocate the table */
|
|
NewHashTable = HeapAlloc(GlobalHeap,
|
|
0,
|
|
FIELD_OFFSET(WSH_HASH_TABLE,
|
|
Handles[HandleCount]));
|
|
|
|
/* Hopefully we have one now */
|
|
if (NewHashTable)
|
|
{
|
|
/* Set its size */
|
|
NewHashTable->Size = HandleCount;
|
|
|
|
/* Initialize it */
|
|
RtlZeroMemory(NewHashTable->Handles, HandleCount * sizeof(PVOID));
|
|
|
|
/* Insert us first */
|
|
WSH_HASH_FROM_HANDLE(Handle->Handle, NewHashTable) = Handle;
|
|
|
|
/* Now check if our old table had entries in it */
|
|
if (HashTable)
|
|
{
|
|
/* We need to move them */
|
|
for (i = 0; i < HashTable->Size; i++)
|
|
{
|
|
/* Make sure the hash handle exists */
|
|
if (HashTable->Handles[i])
|
|
{
|
|
/* Get it */
|
|
HashHandle = &WSH_HASH_FROM_HANDLE(HashTable->
|
|
Handles[i]->Handle,
|
|
NewHashTable);
|
|
|
|
/* Check if it has a value */
|
|
if (!(*HashHandle))
|
|
{
|
|
/* It's empty, so just write the handle */
|
|
*HashHandle = HashTable->Handles[i];
|
|
}
|
|
else
|
|
{
|
|
/* Not empty :/... that implies a collision */
|
|
HeapFree(GlobalHeap, 0, NewHashTable);
|
|
goto ExpandTable;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Write the new hash table */
|
|
SearchTable->HashTable = NewHashTable;
|
|
|
|
/* Wait for everyone to be done with it, then free it */
|
|
TryWaitForReaders(SearchTable);
|
|
HeapFree(GlobalHeap, 0, HashTable);
|
|
}
|
|
else
|
|
{
|
|
/* It was empty, nothing to worry about */
|
|
SearchTable->HashTable = NewHashTable;
|
|
}
|
|
|
|
/* Save the old handle */
|
|
OldHandle = Handle;
|
|
}
|
|
else
|
|
{
|
|
/* There was no old handle */
|
|
OldHandle = Handle;
|
|
}
|
|
} while (0);
|
|
|
|
/* Mark us as free, and release the write lock */
|
|
SearchTable->Expanding = FALSE;
|
|
ReleaseWriteLock(SearchTable);
|
|
break;
|
|
} while (1);
|
|
|
|
/* Return the old handle */
|
|
return OldHandle;
|
|
}
|
|
|
|
PWAH_HANDLE
|
|
WINAPI
|
|
WahReferenceContextByHandle(IN PWAH_HANDLE_TABLE Table,
|
|
IN HANDLE Handle)
|
|
{
|
|
PWAH_HANDLE HashHandle;
|
|
PWAH_SEARCH_TABLE SearchTable;
|
|
PWAH_HASH_TABLE HashTable;
|
|
PVLONG Count;
|
|
|
|
/* Get the current Search Table */
|
|
SearchTable = WSH_SEARCH_TABLE_FROM_HANDLE(Handle, Table);
|
|
|
|
/* Lock it */
|
|
AcquireReadLock(SearchTable, &Count);
|
|
|
|
/* Get the hash table and handle */
|
|
HashTable = SearchTable->HashTable;
|
|
|
|
/* Check if it's valid, and if it's the one we want */
|
|
if ((HashTable) &&
|
|
(HashHandle = WSH_HASH_FROM_HANDLE(Handle, HashTable)) &&
|
|
(HashHandle->Handle == Handle))
|
|
{
|
|
/* Reference the handle */
|
|
InterlockedIncrement(&HashHandle->RefCount);
|
|
}
|
|
else
|
|
{
|
|
/* Invalid handle */
|
|
HashHandle = NULL;
|
|
}
|
|
|
|
/* Release the lock */
|
|
ReleaseReadLock(SearchTable, Count);
|
|
|
|
/* Return */
|
|
return HashHandle;
|
|
}
|
|
|
|
DWORD
|
|
WINAPI
|
|
WahRemoveHandleContext(IN PWAH_HANDLE_TABLE Table,
|
|
IN PWAH_HANDLE Handle)
|
|
{
|
|
PWAH_HANDLE *HashHandle;
|
|
PWAH_SEARCH_TABLE SearchTable;
|
|
PWAH_HASH_TABLE HashTable;
|
|
DWORD ErrorCode = ERROR_SUCCESS;
|
|
|
|
/* Get the current Search Table */
|
|
SearchTable = WSH_SEARCH_TABLE_FROM_HANDLE(Handle->Handle, Table);
|
|
|
|
/* Lock it */
|
|
AcquireWriteLock(SearchTable);
|
|
|
|
/* Get the hash table and handle */
|
|
HashTable = SearchTable->HashTable;
|
|
HashHandle = &WSH_HASH_FROM_HANDLE(Handle->Handle, HashTable);
|
|
|
|
/* Make sure we have a handle, and write the new pointer */
|
|
if (HashHandle && (InterlockedCompareExchangePointer((PVOID*)HashHandle,
|
|
NULL,
|
|
Handle) == Handle))
|
|
{
|
|
/* Wait for everyone to be done with it */
|
|
TryWaitForReaders(SearchTable);
|
|
}
|
|
else
|
|
{
|
|
/* Invalid handle */
|
|
ErrorCode = ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Release the lock */
|
|
ReleaseWriteLock(SearchTable);
|
|
|
|
/* Return */
|
|
return ErrorCode;
|
|
}
|
|
|
|
/* EOF */
|