Leaked source code of windows server 2003
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.

218 lines
6.7 KiB

  1. /*++
  2. Copyright (c) 1998-2000 Microsoft Corporation
  3. Module Name :
  4. readmost.hxx
  5. Abstract:
  6. Read-mostly Data Cache
  7. Author:
  8. George V. Reilly (GeorgeRe) 14-Sep-1998
  9. (from an idea by Neel Jain)
  10. Environment:
  11. Win32 - User Mode
  12. Project:
  13. Internet Information Server RunTime Library
  14. Revision History:
  15. --*/
  16. #ifndef __READMOST_HXX__
  17. #define __READMOST_HXX__
  18. #include "irtlmisc.h"
  19. //=====================================================================
  20. // Locks are expensive and they kill concurrency on multiprocessor
  21. // systems. CDataCache<_T> is a lock-free cache that is suitable for
  22. // "read-mostly" data structures; i.e., data structures that are hardly
  23. // ever updated. We use a monotonically increasing sequence number to
  24. // version stamp the data in the cache. Whenever the data is altered
  25. // (which can only happen through the Write() method), the version number
  26. // is updated. For a Read(), if the version number is the same both
  27. // before and after the data itself is copied into an out parameter, then
  28. // the Read() obtained a valid copy of the data.
  29. //=====================================================================
  30. // Use a portable implementation with interlocked routines that doesn't
  31. // rely on processor-specific memory barrier intrinsics?
  32. #undef READMOST_INTERLOCKED
  33. #ifndef READMOST_INTERLOCKED
  34. #if defined(_M_IA64)
  35. extern "C" void __mf(void);
  36. #pragma intrinsic(__mf)
  37. #endif // _M_IA64
  38. #endif // !READMOST_INTERLOCKED
  39. template <class _T>
  40. class IRTL_DLLEXP CDataCache
  41. {
  42. protected:
  43. // Place the cached data first to preserve its alignment constraints.
  44. volatile _T m_tData;
  45. // Mark the sequence number (version stamp) as volatile to ensure that
  46. // the compiler doesn't cache its value in a register. Mark it as mutable
  47. // so that we can use the Interlocked operations on the sequence
  48. // number in const member functions.
  49. mutable volatile LONG m_nSequence;
  50. enum {
  51. UPDATING = 0xffffffff, // out-of-band odd value => cache is invalid
  52. INITIAL = UPDATING + 1, // even value
  53. STEP = 2, // ensures m_nSequence will never == UPDATING
  54. BOGUS = UPDATING + STEP,// impossible value, never used
  55. };
  56. #ifdef READMOST_INTERLOCKED
  57. LONG
  58. _ReadSequence() const
  59. {
  60. // Since m_nSequence will never be equal to BOGUS, this
  61. // will atomically read the value of m_nSequence, but not
  62. // modify it. On architectures that need such things, it
  63. // will have the side effect of erecting a read memory
  64. // barrier both before and after reading the value of m_nSequence.
  65. return InterlockedCompareExchange((LONG*) &m_nSequence, BOGUS, BOGUS);
  66. }
  67. #else // !READMOST_INTERLOCKED
  68. // On some systems, such as Alphas and Itaniums, the compiler or
  69. // processor can issue out-of-order (speculative) reads and writes.
  70. // _ReadMemoryBarrier() and _WriteMemoryBarrier() force serialization
  71. // of memory accesses.
  72. static void
  73. _ReadMemoryBarrier()
  74. {
  75. #if defined(_M_IA64)
  76. __mf();
  77. #endif // _M_IA64
  78. }
  79. // Read the value of m_nSequence, imposing memory barriers
  80. // both before and after reading m_nSequence.
  81. LONG
  82. _ReadSequence() const
  83. {
  84. _ReadMemoryBarrier();
  85. const LONG nSequence = m_nSequence;
  86. _ReadMemoryBarrier();
  87. return nSequence;
  88. }
  89. // Not currently used, as we rely on InterlockedExchange in
  90. // _SetSequence to do the right thing with write memory barriers.
  91. static void
  92. _WriteMemoryBarrier()
  93. {
  94. #if defined(_M_IA64)
  95. __mf();
  96. #endif // _M_IA64
  97. }
  98. #endif // !READMOST_INTERLOCKED
  99. // Update m_nSequence, returning its old value. InterlockedExchange
  100. // has the side effect of erecting a write memory barrier both
  101. // before and after updating m_nSequence.
  102. LONG
  103. _SetSequence(
  104. LONG nNewValue)
  105. {
  106. return InterlockedExchange((LONG*) &m_nSequence, nNewValue);
  107. }
  108. public:
  109. // Default ctor. Rely on _T::_T() to do something useful.
  110. CDataCache()
  111. : m_nSequence(INITIAL)
  112. {}
  113. // Ctor.
  114. CDataCache(const _T& t)
  115. : m_tData(t), m_nSequence(INITIAL)
  116. {}
  117. // Read the contents of the cache into rtOut. Returns `true' if
  118. // successful, `false' otherwise (in which case rtOut is garbage).
  119. // You should retry if Read() returns `false'.
  120. bool
  121. Read(
  122. _T& rtOut) const
  123. {
  124. const LONG nSequence1 = _ReadSequence();
  125. // Is the data being updated on another thread?
  126. if (nSequence1 != UPDATING)
  127. {
  128. // No, so read the data into rtOut.
  129. // The weird const_cast syntax is necessitated by the volatile
  130. // attribute on m_tData.
  131. rtOut = * const_cast<_T*>(&m_tData);
  132. // If the sequence number is unchanged, the read was valid.
  133. const LONG nSequence2 = _ReadSequence();
  134. return (nSequence1 == nSequence2);
  135. }
  136. // Another thread was updating the cache, so Read failed.
  137. // The caller should probably retry.
  138. return false;
  139. }
  140. // Updates the contents of the cache. Returns `true' if the cache was
  141. // successfully updated, `false' otherwise (because the cache is already
  142. // being updated on some other thread).
  143. bool
  144. Write(
  145. const _T& rtIn)
  146. {
  147. // Atomically set m_nSequence to UPDATING.
  148. const LONG nSequence = _SetSequence(UPDATING);
  149. // If the old value of m_nSequence was not UPDATING,
  150. // then we now "own" the cache.
  151. if (nSequence != UPDATING)
  152. {
  153. // Update the cached data. The weird const_cast syntax is
  154. // necessitated by the volatile attribute on m_tData.
  155. * const_cast<_T*>(&m_tData) = rtIn;
  156. // Finally, update the sequence number. The implicit
  157. // memory barriers in InterlockedExchange will force
  158. // the write of m_tData to complete before m_nSequence
  159. // acquires its new value, and will force the write
  160. // of m_nSequence to complete before Write() returns.
  161. _SetSequence(nSequence + STEP);
  162. return true;
  163. }
  164. // Another thread already owned the cache, so Write failed.
  165. // This is probably fine, but that determination must be
  166. // made by the routine that called Write(), since it
  167. // understands the semantics of its caching and Write() doesn't.
  168. return false;
  169. }
  170. };
  171. #endif // __READMOST_HXX__