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.

393 lines
11 KiB

  1. /***************************************************************************\
  2. *
  3. * File: Thread.cpp
  4. *
  5. * Description:
  6. * This file implements the main Thread that is maintained by the
  7. * ResourceManager to store per-thread information.
  8. *
  9. *
  10. * History:
  11. * 4/18/2000: JStall: Created
  12. *
  13. * Copyright (C) 2000 by Microsoft Corporation. All rights reserved.
  14. *
  15. \***************************************************************************/
  16. #include "stdafx.h"
  17. #include "Services.h"
  18. #include "Thread.h"
  19. #include "Context.h"
  20. #if !USE_DYNAMICTLS
  21. __declspec(thread) Thread * t_pThread;
  22. #endif
  23. /***************************************************************************\
  24. *****************************************************************************
  25. *
  26. * class Thread
  27. *
  28. *****************************************************************************
  29. \***************************************************************************/
  30. //------------------------------------------------------------------------------
  31. Thread::~Thread()
  32. {
  33. m_fStartDestroy = TRUE;
  34. //
  35. // NOTE: The Thread object may be destroyed on its own thread or on another
  36. // thread. Therefore, t_pThread may or may not be == this.
  37. //
  38. //
  39. // Notify the Context that one less thread is using it. Want to do near
  40. // the end since the Context heap may be destroyed after calling this.
  41. // This means that (new / delete) will no longer be valid.
  42. //
  43. // Need to call Context::xwUnlock() directly because
  44. // Context::DeleteObject() will call the ResourceManager to destroy the
  45. // Thread (which is where we already are.)
  46. //
  47. //
  48. // NOTE: We can only destroy the SubThread's when the Thread has a Context.
  49. // This is because destruction of the SubThread's is an "xw" function that
  50. // requires a Context. This unfortunately means that if we were unable
  51. // to create the Context, we're going to leak the SubTread's, but there is
  52. // little we can do about it.
  53. //
  54. m_poolReturn.Destroy();
  55. if (m_pContext != NULL) {
  56. if (m_pContext->xwUnlockNL(xwContextFinalUnlockProc, this)) {
  57. xwDestroySubThreads();
  58. }
  59. m_pContext = NULL;
  60. }
  61. //
  62. // Cleanup cached GDI objects
  63. //
  64. if (hrgnClip != NULL) {
  65. DeleteObject(hrgnClip);
  66. }
  67. //
  68. // NOTE: When m_lstReturn's destructor is called, it will check that all
  69. // memory has been returned. It is possible this may not be empty if memory
  70. // was returned after we emptied m_lstReturn in xwDestroySubThreads(). This
  71. // is an application error since the memory wasn't allocated using
  72. // SGM_RECEIVECONTEXT.
  73. //
  74. // This is actually a serious application problem, since T2 is still using
  75. // memory owned by T1 when T1 is being destroyed. Unfortunately, DirectUser
  76. // can not really do that much about it since the application is using DUser
  77. // in an invalid manner and there are significant performance costs and
  78. // design complications by changing this.
  79. //
  80. }
  81. //------------------------------------------------------------------------------
  82. void
  83. Thread::xwDestroySubThreads()
  84. {
  85. if (m_fDestroySubThreads) {
  86. return;
  87. }
  88. m_fDestroySubThreads = TRUE;
  89. //
  90. // Notify the sub-threads that the Thread and (potentially) the Context
  91. // are being destroyed. This gives them an opportunity to perform any
  92. // necessary callbacks to the application.
  93. //
  94. for (int idx = 0; idx < slCOUNT; idx++) {
  95. if (m_rgSTs[idx] != NULL) {
  96. ProcessDelete(SubThread, m_rgSTs[idx]);
  97. m_rgSTs[idx] = NULL;
  98. }
  99. }
  100. //
  101. // Destroy any other objects that may depend on the Context (and the
  102. // Context heap).
  103. //
  104. m_GdiCache.Destroy();
  105. m_manBuffer.Destroy();
  106. m_heapTemp.Destroy();
  107. //
  108. // Clean up any outstanding returned memory. We need to keep track of all
  109. // the memory this Thread gives out since we can not go away until it has
  110. // all returned. If we were to go away before then, the heap would be
  111. // destroyed and the other Thread would be using bad data.
  112. //
  113. // Therefore, we will make an attempt to get all of the memory back. If
  114. // it takes longer than one minute, we'll have to bail.
  115. //
  116. int cAttempts = 60 * 1000; // Wait a maximum of 60 seconds
  117. while ((m_cMemAlloc > 0) && (cAttempts-- > 0)) {
  118. while (!m_lstReturn.IsEmptyNL()) {
  119. ReturnAllMemoryNL();
  120. }
  121. if (m_cMemAlloc > 0) {
  122. Sleep(1);
  123. }
  124. }
  125. m_poolReturn.Destroy();
  126. }
  127. //------------------------------------------------------------------------------
  128. void CALLBACK
  129. Thread::xwContextFinalUnlockProc(BaseObject * pobj, void * pvData)
  130. {
  131. Thread * pthr = reinterpret_cast<Thread *> (pvData);
  132. Context * pctx = static_cast<Context *> (pobj);
  133. pctx->xwPreDestroyNL();
  134. pthr->xwDestroySubThreads();
  135. }
  136. //------------------------------------------------------------------------------
  137. HRESULT
  138. Thread::Build(
  139. IN BOOL fSRT, // Thread is an SRT
  140. OUT Thread ** ppthrNew) // Newly created thread
  141. {
  142. //
  143. // Check if this Thread is already initialized
  144. //
  145. #if USE_DYNAMICTLS
  146. Thread * pThread = reinterpret_cast<Thread *> (TlsGetValue(g_tlsThread));
  147. if (pThread != NULL) {
  148. *ppthrNew = pThread;
  149. #else
  150. Thread * pThread = t_pThread;
  151. if (pThread != NULL) {
  152. *ppthrNew = pThread;
  153. #endif
  154. return S_OK;
  155. }
  156. HRESULT hr = E_INVALIDARG;
  157. //
  158. // Create a new Thread
  159. //
  160. pThread = ProcessNew(Thread);
  161. if (pThread == NULL) {
  162. hr = E_OUTOFMEMORY;
  163. goto ErrorExit;
  164. }
  165. pThread->hrgnClip = CreateRectRgn(0, 0, 0, 0);
  166. if (pThread->hrgnClip == NULL) {
  167. hr = E_OUTOFMEMORY;
  168. goto ErrorExit;
  169. }
  170. pThread->m_fSRT = fSRT;
  171. //
  172. // Initialize each of the sub-contexts. These can safely use the heap
  173. // which has already been initialized.
  174. //
  175. {
  176. for (int idx = 0; idx < slCOUNT; idx++) {
  177. ThreadPackBuilder * pBuilder = ThreadPackBuilder::GetBuilder((Thread::ESlot) idx);
  178. AssertMsg(pBuilder != NULL, "Builder not initialized using INIT_SUBTHREAD");
  179. SubThread * pST = pBuilder->New(pThread);
  180. pThread->m_rgSTs[idx] = pST;
  181. if ((pThread->m_rgSTs[idx] == NULL) ||
  182. FAILED(hr = pThread->m_rgSTs[idx]->Create())) {
  183. goto ErrorExit;
  184. }
  185. }
  186. }
  187. AssertMsg(pThread != NULL, "Ensure Thread is valid");
  188. #if USE_DYNAMICTLS
  189. AssertMsg(TlsGetValue(g_tlsThread) == NULL, "Ensure TLS is still empty");
  190. Verify(TlsSetValue(g_tlsThread, pThread));
  191. #else
  192. AssertMsg(t_pThread == NULL, "Ensure TLS is still empty");
  193. t_pThread = pThread;
  194. #endif
  195. *ppthrNew = pThread;
  196. return S_OK;
  197. ErrorExit:
  198. //
  199. // An error occurred while initializing the thread, so need to tear down
  200. // the object.
  201. //
  202. if (pThread != NULL) {
  203. if (pThread->hrgnClip != NULL) {
  204. DeleteObject(pThread->hrgnClip);
  205. }
  206. if (pThread != NULL) {
  207. ProcessDelete(Thread, pThread);
  208. }
  209. }
  210. *ppthrNew = NULL;
  211. return hr;
  212. }
  213. //------------------------------------------------------------------------------
  214. ReturnMem *
  215. Thread::AllocMemoryNL(
  216. IN int cbSize) // Size of allocating, including ReturnMem
  217. {
  218. AssertMsg(cbSize >= sizeof(ReturnMem),
  219. "Allocation must be at least sizeof(ReturnMem)");
  220. AssertMsg(!m_fDestroySubThreads, "Must be before subtreads start destruction");
  221. //
  222. // Before allocating memory from our pool, return memory back to the pool if
  223. // the pool is already empty. Only do this if the pool is empty.
  224. // Otherwise, the effort is unnecessary and just slows things down.
  225. //
  226. if (m_poolReturn.IsEmpty()) {
  227. ReturnAllMemoryNL();
  228. }
  229. //
  230. // Now, allocate the memory. Allocate from our pool if it is within the
  231. // pool size.
  232. //
  233. ReturnMem * prMem;
  234. if (cbSize <= POOLBLOCK_SIZE) {
  235. cbSize = POOLBLOCK_SIZE;
  236. prMem = m_poolReturn.New();
  237. } else {
  238. prMem = reinterpret_cast<ReturnMem *> (ContextAlloc(GetContext()->GetHeap(), cbSize));
  239. }
  240. if (prMem != NULL) {
  241. prMem->cbSize = cbSize;
  242. m_cMemAlloc++;
  243. }
  244. return prMem;
  245. }
  246. //------------------------------------------------------------------------------
  247. void
  248. Thread::ReturnAllMemoryNL()
  249. {
  250. //
  251. // Check if any memory has been returned. If so, we need to add it back
  252. // into the pool if it is the right size. Only need to ExtractNL() once.
  253. // If we loop, we unnecessarily hit the S-List memory, causing more
  254. // slow-downs.
  255. //
  256. // As we return the memory, we decrement the number of outstanding
  257. // allocations to keep track of how many are remaining.
  258. //
  259. ReturnMem * pNode = m_lstReturn.ExtractNL();
  260. while (pNode != NULL) {
  261. ReturnMem * pNext = pNode->pNext;
  262. if (pNode->cbSize == POOLBLOCK_SIZE) {
  263. PoolMem * pPoolMem = static_cast<PoolMem *> (pNode);
  264. m_poolReturn.Delete(pPoolMem);
  265. } else {
  266. ContextFree(GetContext()->GetHeap(), pNode);
  267. }
  268. AssertMsg(m_cMemAlloc > 0, "Must have a remaining memory allocation");
  269. m_cMemAlloc--;
  270. pNode = pNext;
  271. }
  272. }
  273. #if DBG
  274. //------------------------------------------------------------------------------
  275. void
  276. Thread::DEBUG_AssertValid() const
  277. {
  278. Assert(hrgnClip != NULL);
  279. AssertInstance(m_pContext);
  280. for (int idx = 0; idx < slCOUNT; idx++) {
  281. AssertInstance(m_rgSTs[idx]);
  282. }
  283. if (!m_fStartDestroy) {
  284. Assert(m_cRef > 0);
  285. Assert(!m_fDestroySubThreads);
  286. }
  287. }
  288. #endif
  289. /***************************************************************************\
  290. *****************************************************************************
  291. *
  292. * class SubThread
  293. *
  294. *****************************************************************************
  295. \***************************************************************************/
  296. #if DBG
  297. //------------------------------------------------------------------------------
  298. void
  299. SubThread::DEBUG_AssertValid() const
  300. {
  301. // Don't use AssertInstance since it would be recursive.
  302. Assert(m_pParent != NULL);
  303. }
  304. #endif
  305. /***************************************************************************\
  306. *****************************************************************************
  307. *
  308. * class ThreadPackBuilder
  309. *
  310. *****************************************************************************
  311. \***************************************************************************/
  312. PREINIT_SUBTHREAD(CoreST);
  313. ThreadPackBuilder * ThreadPackBuilder::s_rgBuilders[Thread::slCOUNT] =
  314. {
  315. INIT_SUBTHREAD(CoreST),
  316. };