Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

116 lines
3.0 KiB

  1. ///////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright (c) 1998, Microsoft Corp. All rights reserved.
  4. //
  5. // FILE
  6. //
  7. // Perimeter.cpp
  8. //
  9. // SYNOPSIS
  10. //
  11. // This file implements the class Perimeter.
  12. //
  13. // MODIFICATION HISTORY
  14. //
  15. // 09/04/1997 Original version.
  16. // 09/30/1998 Fix bug with recursive LockExclusive calls.
  17. //
  18. ///////////////////////////////////////////////////////////////////////////////
  19. #include <ias.h>
  20. #include <climits>
  21. #include <Perimeter.h>
  22. //////////
  23. // Large negative value used to block shared entry into the perimeter.
  24. //////////
  25. const LONG BLOCK_VALUE = (-LONG_MAX)/2;
  26. Perimeter::Perimeter() throw ()
  27. : sharing(0),
  28. waiting(0),
  29. count(&sharing)
  30. {
  31. InitializeCriticalSection(&exclusive);
  32. sharedOK = CreateSemaphore(NULL, 0, LONG_MAX, NULL);
  33. exclusiveOK = CreateSemaphore(NULL, 0, 1, NULL);
  34. }
  35. Perimeter::~Perimeter() throw ()
  36. {
  37. CloseHandle(exclusiveOK);
  38. CloseHandle(sharedOK);
  39. DeleteCriticalSection(&exclusive);
  40. }
  41. void Perimeter::Lock() throw ()
  42. {
  43. // If this is less than zero, then an exlusive thread must have inserted
  44. // the BLOCK_VALUE, so ...
  45. if (InterlockedIncrement(count) <= 0)
  46. {
  47. // ... we have to wait until he's done.
  48. WaitForSingleObject(sharedOK, INFINITE);
  49. }
  50. }
  51. void Perimeter::LockExclusive() throw ()
  52. {
  53. // This limits exclusive access to a single thread.
  54. EnterCriticalSection(&exclusive);
  55. // The first time through we have to wait for the sharers to finish.
  56. if (exclusive.RecursionCount == 1)
  57. {
  58. // Block any new shared threads.
  59. waiting = BLOCK_VALUE;
  60. InterlockedExchangePointer((PVOID *)&count, &waiting);
  61. // Find out how many shared threads are already in the perimeter ...
  62. LONG sharingNow = InterlockedExchangeAdd(&sharing, BLOCK_VALUE);
  63. if (sharingNow > 0)
  64. {
  65. // ... and wait until they're done.
  66. WaitForSingleObject(exclusiveOK, INFINITE);
  67. }
  68. // At this point there is no one left inside the perimeter.
  69. sharing = 0;
  70. }
  71. }
  72. void Perimeter::Unlock() throw ()
  73. {
  74. // If sharing is zero, we must be an exclusive thread.
  75. if (!sharing)
  76. {
  77. // Are we about to release our last lock ?
  78. if (exclusive.RecursionCount == 1)
  79. {
  80. // Allow any new shared access attempts.
  81. InterlockedExchangePointer((PVOID *)&count, &sharing);
  82. // Find out how many threads are waiting on the semaphore ...
  83. LONG waitingNow = waiting - BLOCK_VALUE;
  84. if (waitingNow > 0)
  85. {
  86. // ... and let them go.
  87. InterlockedExchangeAdd(count, waitingNow);
  88. ReleaseSemaphore(sharedOK, waitingNow, NULL);
  89. }
  90. }
  91. // Release the exclusive lock.
  92. LeaveCriticalSection(&exclusive);
  93. }
  94. else if (InterlockedDecrement(&sharing) == BLOCK_VALUE)
  95. {
  96. // If we end up here, we must have been the last shared thread out of
  97. // the perimeter while an exlusive thread is waiting, so wake him up.
  98. ReleaseSemaphore(exclusiveOK, 1, NULL) ;
  99. }
  100. }