ThreadServiceWrapperBase.cpp 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. #include "RuntimeBasePch.h"
  6. #include "Base/ThreadServiceWrapperBase.h"
  7. ThreadServiceWrapperBase::ThreadServiceWrapperBase() :
  8. threadContext(nullptr),
  9. needIdleCollect(false),
  10. inIdleCollect(false),
  11. hasScheduledIdleCollect(false),
  12. shouldScheduleIdleCollectOnExitIdle(false),
  13. forceIdleCollectOnce(false)
  14. {
  15. }
  16. bool ThreadServiceWrapperBase::Initialize(ThreadContext *newThreadContext)
  17. {
  18. if (newThreadContext == nullptr)
  19. {
  20. return false;
  21. }
  22. threadContext = newThreadContext;
  23. threadContext->SetThreadServiceWrapper(this);
  24. return true;
  25. }
  26. void ThreadServiceWrapperBase::Shutdown()
  27. {
  28. if (hasScheduledIdleCollect)
  29. {
  30. #if DBG
  31. // Fake the inIdleCollect to get pass asserts in FinishIdleCollect
  32. inIdleCollect = true;
  33. #endif
  34. FinishIdleCollect(FinishReason::FinishReasonNormal);
  35. }
  36. }
  37. bool ThreadServiceWrapperBase::ScheduleIdleCollect(uint ticks, bool scheduleAsTask)
  38. {
  39. Assert(!threadContext->IsInScript());
  40. // We should schedule have called this in one of two cases:
  41. // 1) Either needIdleCollect is true- in which case, we should schedule one
  42. // 2) Or ScheduleNextCollectionOnExit was called when needIdleCollect was true, but we didn't schedule
  43. // one because we were at the time in one. Later, as we unwound, we might have set needIdleCollect to false
  44. // but because we had noted that we needed to schedule a collect, we would end up coming into this function
  45. // so allow for that
  46. Assert(needIdleCollect || shouldScheduleIdleCollectOnExitIdle || threadContext->GetRecycler()->CollectionInProgress());
  47. if (!CanScheduleIdleCollect())
  48. {
  49. return false;
  50. }
  51. if (hasScheduledIdleCollect)
  52. {
  53. return true;
  54. }
  55. if (OnScheduleIdleCollect(ticks, scheduleAsTask))
  56. {
  57. JS_ETW(EventWriteJSCRIPT_GC_IDLE_START(this));
  58. IDLE_COLLECT_VERBOSE_TRACE(_u("ScheduledIdleCollect- Set hasScheduledIdleCollect\n"));
  59. hasScheduledIdleCollect = true;
  60. return true;
  61. }
  62. else
  63. {
  64. IDLE_COLLECT_TRACE(_u("Idle timer setup failed\n"));
  65. FinishIdleCollect(FinishReason::FinishReasonIdleTimerSetupFailed);
  66. return false;
  67. }
  68. }
  69. bool ThreadServiceWrapperBase::IdleCollect()
  70. {
  71. // Tracking service does not AddRef/Release the thread service and only keeps a function pointer and context parameter (this pointer)
  72. // to execute the IdleCollect callback. It is possible that the tracking service gets destroyed as part of the collection
  73. // during this IdleCollect. If that happens then we need to make sure ThreadService (which may be owned by the tracking service)
  74. // is kept alive until this callback completes. Any pending timer is killed in the thread service destructor so we should not get
  75. // any new callbacks after the thread service is destroyed.
  76. AutoAddRefReleaseThreadService autoThreadServiceKeepAlive(this);
  77. Assert(hasScheduledIdleCollect);
  78. IDLE_COLLECT_VERBOSE_TRACE(_u("IdleCollect- reset hasScheduledIdleCollect\n"));
  79. hasScheduledIdleCollect = false;
  80. // Don't do anything and kill the timer if we are called recursively or if we are in script
  81. if (inIdleCollect || threadContext->IsInScript())
  82. {
  83. FinishIdleCollect(FinishReason::FinishReasonNormal);
  84. return hasScheduledIdleCollect;
  85. }
  86. // If during idle collect we determine that we need to schedule another
  87. // idle collect, this gets flipped to true
  88. shouldScheduleIdleCollectOnExitIdle = false;
  89. AutoBooleanToggle autoInIdleCollect(&inIdleCollect);
  90. Recycler* recycler = threadContext->GetRecycler();
  91. #if ENABLE_CONCURRENT_GC
  92. // Finish concurrent on timer heart beat if needed
  93. // We wouldn't try to finish if we need to schedule
  94. // an idle task to finish the collection
  95. if (this->ShouldFinishConcurrentCollectOnIdleCallback() && recycler->FinishConcurrent<FinishConcurrentOnIdle>())
  96. {
  97. IDLE_COLLECT_TRACE(_u("Idle callback: finish concurrent\n"));
  98. JS_ETW(EventWriteJSCRIPT_GC_IDLE_CALLBACK_FINISH(this));
  99. }
  100. #endif
  101. while (true)
  102. {
  103. // If a GC is still happening, just wait for the next heart beat
  104. if (recycler->CollectionInProgress())
  105. {
  106. ScheduleIdleCollect(IdleTicks, true /* schedule as task */);
  107. break;
  108. }
  109. // If there no more need of idle collect, then cancel the timer
  110. if (!needIdleCollect)
  111. {
  112. FinishIdleCollect(FinishReason::FinishReasonNormal);
  113. break;
  114. }
  115. int timeDiff = tickCountNextIdleCollection - GetTickCount();
  116. // See if we pass the time for the next scheduled Idle GC
  117. if (timeDiff > 0)
  118. {
  119. // Not time yet, wait for the next heart beat
  120. ScheduleIdleCollect(IdleTicks, false /* not schedule as task */);
  121. IDLE_COLLECT_TRACE(_u("Idle callback: nop until next collection: %d\n"), timeDiff);
  122. break;
  123. }
  124. // activate an idle collection
  125. IDLE_COLLECT_TRACE(_u("Idle callback: collection: %d\n"), timeDiff);
  126. JS_ETW(EventWriteJSCRIPT_GC_IDLE_CALLBACK_NEWCOLLECT(this));
  127. needIdleCollect = false;
  128. recycler->CollectNow<CollectOnScriptIdle>();
  129. }
  130. if (shouldScheduleIdleCollectOnExitIdle)
  131. {
  132. ScheduleIdleCollect(IdleTicks, false /* not schedule as task */);
  133. }
  134. return hasScheduledIdleCollect;
  135. }
  136. void ThreadServiceWrapperBase::FinishIdleCollect(ThreadServiceWrapperBase::FinishReason reason)
  137. {
  138. Assert(reason == FinishReason::FinishReasonIdleTimerSetupFailed ||
  139. reason == FinishReason::FinishReasonTaskComplete ||
  140. inIdleCollect || threadContext->IsInScript() || !threadContext->GetRecycler()->CollectionInProgress());
  141. IDLE_COLLECT_VERBOSE_TRACE(_u("FinishIdleCollect- Reset hasScheduledIdleCollect\n"));
  142. hasScheduledIdleCollect = false;
  143. needIdleCollect = false;
  144. OnFinishIdleCollect();
  145. IDLE_COLLECT_TRACE(_u("Idle timer finished\n"));
  146. if (reason == FinishReason::FinishReasonTaskComplete
  147. && threadContext->GetRecycler()->CollectionInProgress()
  148. && !threadContext->IsInScript())
  149. {
  150. // schedule another timer to check the progress
  151. IDLE_COLLECT_TRACE(_u("FinishIdleCollect- collection is still in progress, schedule another timer to check the status\n"));
  152. // schedule a shorter timer here to check the progress since the idle GC has already last more than one second and is likely to close to finish
  153. // TODO: measure and adjust the timeout bellow
  154. ScheduleIdleCollect(RecyclerHeuristic::TickCountIdleCollectRepeatTimer, false);
  155. }
  156. else
  157. {
  158. JS_ETW(EventWriteJSCRIPT_GC_IDLE_FINISHED(this));
  159. }
  160. }
  161. bool ThreadServiceWrapperBase::ScheduleNextCollectOnExit()
  162. {
  163. Assert(!threadContext->IsInScript());
  164. Assert(!needIdleCollect || hasScheduledIdleCollect);
  165. Recycler* recycler = threadContext->GetRecycler();
  166. #if ENABLE_CONCURRENT_GC
  167. recycler->FinishConcurrent<FinishConcurrentOnExitScript>();
  168. #endif
  169. #ifdef RECYCLER_TRACE
  170. bool oldNeedIdleCollect = needIdleCollect;
  171. if (forceIdleCollectOnce)
  172. {
  173. IDLE_COLLECT_VERBOSE_TRACE(_u("Need to force one idle collection\n"));
  174. }
  175. #endif
  176. needIdleCollect = forceIdleCollectOnce || recycler->ShouldIdleCollectOnExit();
  177. if (needIdleCollect)
  178. {
  179. // Set up when we will do the idle decommit
  180. tickCountNextIdleCollection = GetTickCount() + IdleTicks;
  181. IDLE_COLLECT_VERBOSE_TRACE(_u("Idle on exit collect %s: %d\n"), (oldNeedIdleCollect ? _u("rescheduled") : _u("scheduled")),
  182. tickCountNextIdleCollection - GetTickCount());
  183. JS_ETW(EventWriteJSCRIPT_GC_IDLE_SCHEDULED(this));
  184. }
  185. else
  186. {
  187. IDLE_COLLECT_VERBOSE_TRACE(_u("Idle on exit collect %s\n"), oldNeedIdleCollect ? _u("cancelled") : _u("not scheduled"));
  188. if (!recycler->CollectionInProgress())
  189. {
  190. // We collected and finished, no need to ensure the idle collect call back.
  191. return true;
  192. }
  193. IDLE_COLLECT_VERBOSE_TRACE(_u("Idle on exit collect %s\n"), hasScheduledIdleCollect || oldNeedIdleCollect ? _u("reschedule finish") : _u("schedule finish"));
  194. }
  195. // Don't schedule the call back if we are already in idle call back, as we don't do anything on recursive call anyways
  196. // IdleCollect will schedule one if necessary
  197. if (inIdleCollect)
  198. {
  199. shouldScheduleIdleCollectOnExitIdle = true;
  200. return true;
  201. }
  202. else
  203. {
  204. return ScheduleIdleCollect(IdleTicks, false /* not schedule as task */);
  205. }
  206. }
  207. void ThreadServiceWrapperBase::ClearForceOneIdleCollection()
  208. {
  209. IDLE_COLLECT_VERBOSE_TRACE(_u("Clearing force idle collect flag\n"));
  210. this->forceIdleCollectOnce = false;
  211. }
  212. void ThreadServiceWrapperBase::SetForceOneIdleCollection()
  213. {
  214. IDLE_COLLECT_VERBOSE_TRACE(_u("Setting force idle collect flag\n"));
  215. this->forceIdleCollectOnce = true;
  216. }
  217. void ThreadServiceWrapperBase::ScheduleFinishConcurrent()
  218. {
  219. Assert(!threadContext->IsInScript());
  220. Assert(threadContext->GetRecycler()->CollectionInProgress());
  221. if (!this->inIdleCollect)
  222. {
  223. IDLE_COLLECT_VERBOSE_TRACE(_u("Idle collect %s\n"), needIdleCollect ? _u("reschedule finish") : _u("scheduled finish"));
  224. this->needIdleCollect = false;
  225. ScheduleIdleCollect(IdleFinishTicks, true /* schedule as task */);
  226. }
  227. }