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.

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