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.

315 lines
8.7 KiB

  1. #include "stdinc.h"
  2. /*-----------------------------------------------------------------------------
  3. Fusion Thread Local Storage (aka Per Thread Data)
  4. -----------------------------------------------------------------------------*/
  5. #include "ntrtl.h"
  6. #include "fusiontls.h"
  7. #include "FusionDequeLinkage.h"
  8. #include "FusionDeque.h"
  9. #include "FusionEventLog.h"
  10. #include "FusionHeap.h"
  11. #include "SxsExceptionHandling.h"
  12. //
  13. // Don't use the FusionTrace functionality in this file.
  14. //
  15. // #include "fusiontracelight.h"
  16. //
  17. static DWORD s_dwFusionpThreadLocalIndex = TLS_OUT_OF_INDEXES;
  18. // typedef CDeque<CFusionPerThreadData, offsetof( CFusionPerThreadData, m_Linkage )> CFusionTlsList;
  19. static CRITICAL_SECTION s_TlsCriticalSection;
  20. static LIST_ENTRY * g_FusionTlsList = NULL;
  21. static __int64 storage_g_FusionTlsList[sizeof(LIST_ENTRY)/sizeof(__int64) + 1];
  22. template <typename T>
  23. T* PlacementNew(T* p)
  24. {
  25. (void) (new (reinterpret_cast<PVOID>(p)) T);
  26. return p;
  27. }
  28. BOOL
  29. FusionpPerThreadDataMain(
  30. HINSTANCE hInst,
  31. DWORD dwReason
  32. )
  33. {
  34. BOOL fResult = FALSE;
  35. /*
  36. INTERNAL_ERROR_CHECK(
  37. ( dwReason != DLL_THREAD_ATTACH ) &&
  38. ( dwReason != DLL_THREAD_DETACH )
  39. );
  40. */
  41. switch ( dwReason )
  42. {
  43. case DLL_PROCESS_ATTACH:
  44. __try
  45. {
  46. if ( !::InitializeCriticalSectionAndSpinCount(
  47. &s_TlsCriticalSection,
  48. INITIALIZE_CRITICAL_SECTION_AND_SPIN_COUNT_ALLOCATE_NOW
  49. ) )
  50. {
  51. ::FusionpDbgPrintEx(
  52. FUSION_DBG_LEVEL_ERROR,
  53. "SXS: %s - Failed creating TLS critical section, LastError=%08x\n",
  54. __FUNCTION__,
  55. ::FusionpGetLastWin32Error());
  56. goto Exit;
  57. }
  58. }
  59. __except( EXCEPTION_EXECUTE_HANDLER )
  60. {
  61. //
  62. // The exception code is interesting. However, we don't have access
  63. // (directly) to SxspSetLastNTError yet - this is a hotfix.
  64. //
  65. // REVIEW: Move SxspSetLastNTError into one of the fusion\utils\*.cpp files
  66. // because it seems like a relatively good idea.
  67. //
  68. ::SetLastError(::FusionpNtStatusToDosError(GetExceptionCode()));
  69. goto Exit;
  70. }
  71. s_dwFusionpThreadLocalIndex = TlsAlloc();
  72. if ( s_dwFusionpThreadLocalIndex == TLS_OUT_OF_INDEXES )
  73. {
  74. ::FusionpDbgPrintEx(
  75. FUSION_DBG_LEVEL_ERROR,
  76. "SXS.DLL: %s(): TlsAlloc failed: last error %d\n", __FUNCTION__, FusionpGetLastWin32Error());
  77. goto Exit;
  78. }
  79. g_FusionTlsList = PlacementNew(reinterpret_cast<LIST_ENTRY *>(&storage_g_FusionTlsList));
  80. if ( g_FusionTlsList == NULL )
  81. {
  82. ::FusionpDbgPrintEx(
  83. FUSION_DBG_LEVEL_ERROR,
  84. "SXS.DLL: %s(): Really bad things: placement new of CFusionTlsList failed, 0x%08x\n",
  85. __FUNCTION__,
  86. ::FusionpGetLastWin32Error());
  87. ::SetLastError(ERROR_INTERNAL_ERROR);
  88. goto Exit;
  89. }
  90. InitializeListHead(g_FusionTlsList);
  91. break;
  92. case DLL_PROCESS_DETACH:
  93. if ( s_dwFusionpThreadLocalIndex != TLS_OUT_OF_INDEXES )
  94. {
  95. ::EnterCriticalSection( &s_TlsCriticalSection );
  96. __try
  97. {
  98. LIST_ENTRY *ple = g_FusionTlsList->Flink;
  99. while (ple != g_FusionTlsList)
  100. {
  101. CFusionPerThreadData *pptd = CONTAINING_RECORD(ple, CFusionPerThreadData, m_Linkage);
  102. ple = ple->Flink;
  103. FUSION_DELETE_SINGLETON(pptd);
  104. }
  105. }
  106. __finally
  107. {
  108. ::LeaveCriticalSection( &s_TlsCriticalSection );
  109. }
  110. ::TlsFree( s_dwFusionpThreadLocalIndex );
  111. s_dwFusionpThreadLocalIndex = TLS_OUT_OF_INDEXES;
  112. }
  113. //
  114. // Re-entrance intelligence
  115. //
  116. if (g_FusionTlsList != NULL)
  117. {
  118. InitializeListHead(g_FusionTlsList);
  119. g_FusionTlsList = NULL;
  120. }
  121. ::DeleteCriticalSection( &s_TlsCriticalSection );
  122. break;
  123. }
  124. fResult = TRUE;
  125. Exit:
  126. return fResult;
  127. }
  128. VOID
  129. FusionpClearPerThreadData(
  130. VOID
  131. )
  132. {
  133. CFusionPerThreadData *pPerThread;
  134. PVOID pvPerThread;
  135. ASSERT_NTC(s_dwFusionpThreadLocalIndex != TLS_OUT_OF_INDEXES);
  136. //
  137. // Acquire, then clear, this thread's per-thread data
  138. //
  139. pvPerThread = ::TlsGetValue(s_dwFusionpThreadLocalIndex);
  140. //
  141. // If the TlsSetValue failed with STATUS_NO_MEMORY, that's just dandy.
  142. // Otherwise, something wierd has happened along the line, and maybe
  143. // people care to know.
  144. //
  145. if ( !::TlsSetValue( s_dwFusionpThreadLocalIndex, NULL ) )
  146. {
  147. SOFT_ASSERT_NTC(::FusionpGetLastWin32Error() != ERROR_NOT_ENOUGH_MEMORY);
  148. }
  149. //
  150. // If we got something interesting back from TlsGetValue, then cast it
  151. // to the Right Thing and delete it.
  152. //
  153. pPerThread = reinterpret_cast<CFusionPerThreadData*>(pvPerThread);
  154. if (pPerThread != NULL)
  155. FUSION_DELETE_SINGLETON(pPerThread);
  156. }
  157. CFusionPerThreadData*
  158. FusionpGetPerThreadData(
  159. EFusionpTls e
  160. )
  161. {
  162. const DWORD dwLastError = ::FusionpGetLastWin32Error();
  163. CFusionPerThreadData* pTls = NULL;
  164. __try
  165. {
  166. // the use of "temp" here mimics what you would do with a destructor;
  167. // have a temp that is unconditionally freed, unless it is nulled by commiting it
  168. // into the return value "return pt.Detach();"
  169. CFusionPerThreadData* pTlsTemp = reinterpret_cast<CFusionPerThreadData*>(::TlsGetValue(s_dwFusionpThreadLocalIndex));
  170. if ((pTlsTemp == NULL) && ((e & eFusionpTlsCreate) != 0))
  171. {
  172. if (::FusionpGetLastWin32Error() != NO_ERROR)
  173. {
  174. ::FusionpDbgPrintEx(
  175. FUSION_DBG_LEVEL_ERROR,
  176. "SXS.DLL: %s() called TlsGetValue() which failed; win32 error = %d\n", __FUNCTION__, ::FusionpGetLastWin32Error());
  177. FUSION_DEBUG_BREAK();
  178. return NULL;
  179. }
  180. pTlsTemp = reinterpret_cast<CFusionPerThreadData*>(FUSION_RAW_ALLOC(sizeof(*pTlsTemp), 'tsxs'));
  181. if (pTlsTemp == NULL)
  182. {
  183. ::FusionpDbgPrintEx(
  184. FUSION_DBG_LEVEL_ERROR,
  185. "SXS.DLL: TLS allocation failed in %s()\n", __FUNCTION__);
  186. return NULL;
  187. }
  188. if (!::TlsSetValue(s_dwFusionpThreadLocalIndex, pTlsTemp))
  189. {
  190. ::FusionpDbgPrintEx(
  191. FUSION_DBG_LEVEL_ERROR,
  192. "SXS.DLL: TlsSetValue failed in %s(), lastError: %d\n", __FUNCTION__, FusionpGetLastWin32Error());
  193. FUSION_RAW_DEALLOC(pTlsTemp);
  194. return NULL;
  195. }
  196. ::EnterCriticalSection( &s_TlsCriticalSection );
  197. __try
  198. {
  199. InsertTailList(g_FusionTlsList, &pTlsTemp->m_Linkage);
  200. }
  201. __finally
  202. {
  203. ::LeaveCriticalSection( &s_TlsCriticalSection );
  204. }
  205. }
  206. pTls = pTlsTemp;
  207. pTlsTemp = NULL;
  208. } __finally {
  209. ::SetLastError(dwLastError);
  210. }
  211. return pTls;
  212. }
  213. CFusionPerThreadData *
  214. FusionpSetPerThreadData(
  215. CFusionPerThreadData *pNewTls,
  216. EFusionSetTls Action
  217. )
  218. {
  219. CSxsPreserveLastError ple;
  220. CFusionPerThreadData *pExisting = NULL;
  221. if (s_dwFusionpThreadLocalIndex == TLS_OUT_OF_INDEXES)
  222. {
  223. ::FusionpDbgPrintEx(
  224. FUSION_DBG_LEVEL_ERROR,
  225. "SXS.DLL: Attempt to set per thread data when TLS index was not allocated.");
  226. FUSION_DEBUG_BREAK();
  227. ple.Restore();
  228. return NULL;
  229. }
  230. if ( ( Action != eFusionpTlsSetReplaceExisting ) &&
  231. ( Action != eFusionpTlsSetIfNull ) &&
  232. ( Action != 0 ) )
  233. {
  234. ::FusionpDbgPrintEx(
  235. FUSION_DBG_LEVEL_ERROR,
  236. "SXS.DLL: Invalid action parameter passed to %s\n", __FUNCTION__);
  237. FUSION_DEBUG_BREAK();
  238. ple.Restore();
  239. return NULL;
  240. }
  241. else if (Action == 0)
  242. {
  243. Action = eFusionpTlsSetIfNull;
  244. }
  245. pExisting = ::FusionpGetPerThreadData();
  246. //
  247. // If the existing one is null, and we're supposed to set it
  248. // if it's null, then set it. Or, if we're suppose to set it
  249. // anyhow, then that's fine.
  250. //
  251. if ((Action == eFusionpTlsSetReplaceExisting) ||
  252. ((Action == eFusionpTlsSetIfNull) &&
  253. (pExisting == NULL)))
  254. {
  255. if ( !::TlsSetValue(s_dwFusionpThreadLocalIndex, pNewTls))
  256. {
  257. ple.Restore();
  258. return NULL;
  259. }
  260. pExisting = pNewTls;
  261. }
  262. ple.Restore();
  263. return pExisting;
  264. }