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