Jobs.cpp 50 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427
  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 "CommonCommonPch.h"
  6. #ifdef _WIN32
  7. #include <process.h>
  8. #endif
  9. #include "Core/EtwTraceCore.h"
  10. #include "Exceptions/ExceptionBase.h"
  11. #include "Exceptions/JavascriptException.h"
  12. #include "Exceptions/OperationAbortedException.h"
  13. #include "Exceptions/OutOfMemoryException.h"
  14. #include "Exceptions/StackOverflowException.h"
  15. #include "TemplateParameter.h"
  16. #include "DataStructures/DoublyLinkedListElement.h"
  17. #include "DataStructures/DoublyLinkedList.h"
  18. #include "DataStructures/DoublyLinkedListElement.inl"
  19. #include "DataStructures/DoublyLinkedList.inl"
  20. #include "Common/Event.h"
  21. #include "Common/ThreadService.h"
  22. #include "Common/Jobs.h"
  23. #include "Common/Jobs.inl"
  24. #include "Core/CommonMinMax.h"
  25. #include "Memory/RecyclerWriteBarrierManager.h"
  26. namespace JsUtil
  27. {
  28. // -------------------------------------------------------------------------------------------------------------------------
  29. // Job
  30. // -------------------------------------------------------------------------------------------------------------------------
  31. Job::Job(const bool isCritical) : manager(0), isCritical(isCritical)
  32. #if ENABLE_DEBUG_CONFIG_OPTIONS
  33. , failureReason(FailureReason::NotFailed)
  34. #endif
  35. {
  36. }
  37. Job::Job(JobManager *const manager, const bool isCritical) : manager(manager), isCritical(isCritical)
  38. #if ENABLE_DEBUG_CONFIG_OPTIONS
  39. , failureReason(FailureReason::NotFailed)
  40. #endif
  41. {
  42. Assert(manager);
  43. }
  44. JobManager *Job::Manager() const
  45. {
  46. return manager;
  47. }
  48. bool Job::IsCritical() const
  49. {
  50. return isCritical;
  51. }
  52. // -------------------------------------------------------------------------------------------------------------------------
  53. // JobManager
  54. // -------------------------------------------------------------------------------------------------------------------------
  55. JobManager::JobManager(JobProcessor *const processor)
  56. : processor(processor), numJobsAddedToProcessor(0), isWaitable(false)
  57. {
  58. Assert(processor);
  59. }
  60. JobManager::JobManager(JobProcessor *const processor, const bool isWaitable)
  61. : processor(processor), numJobsAddedToProcessor(0), isWaitable(isWaitable)
  62. {
  63. Assert(processor);
  64. }
  65. JobProcessor *JobManager::Processor() const
  66. {
  67. return processor;
  68. }
  69. void JobManager::LastJobProcessed()
  70. {
  71. }
  72. void JobManager::ProcessorThreadSpecificCallBack(PageAllocator *pageAllocator)
  73. {
  74. }
  75. Job *JobManager::GetJobToProcessProactively()
  76. {
  77. return 0;
  78. }
  79. bool JobManager::ShouldProcessInForeground(const bool willWaitForJob, const unsigned int numJobsInQueue) const
  80. {
  81. return false;
  82. }
  83. void JobManager::Prioritize(JsUtil::Job *const job, const bool forceAddJobToProcessor, void* function) const
  84. {
  85. }
  86. void JobManager::PrioritizedButNotYetProcessed(JsUtil::Job *const job) const
  87. {
  88. }
  89. void JobManager::OnDecommit(ParallelThreadData *threadData)
  90. {
  91. }
  92. void JobManager::BeforeWaitForJob(bool) const
  93. {
  94. }
  95. void JobManager::AfterWaitForJob(bool) const
  96. {
  97. }
  98. // -------------------------------------------------------------------------------------------------------------------------
  99. // WaitableJobManager
  100. // -------------------------------------------------------------------------------------------------------------------------
  101. WaitableJobManager::WaitableJobManager(JobProcessor *const processor)
  102. : JobManager(processor, true),
  103. jobBeingWaitedUpon(0),
  104. #if ENABLE_BACKGROUND_JOB_PROCESSOR
  105. jobBeingWaitedUponProcessed(false),
  106. #endif
  107. isWaitingForQueuedJobs(false)
  108. #if ENABLE_BACKGROUND_JOB_PROCESSOR
  109. , queuedJobsProcessed(false)
  110. #endif
  111. {
  112. }
  113. // -------------------------------------------------------------------------------------------------------------------------
  114. // SingleJobManager
  115. // -------------------------------------------------------------------------------------------------------------------------
  116. SingleJobManager::SingleJobManager(JobProcessor *const processor, const bool isCritical)
  117. : JobManager(processor), job(isCritical), processed(false)
  118. {
  119. job.manager = this;
  120. }
  121. void SingleJobManager::AddJobToProcessor(const bool prioritize)
  122. {
  123. AutoOptionalCriticalSection lock(Processor()->GetCriticalSection());
  124. Processor()->AddJob(&job, prioritize);
  125. }
  126. void SingleJobManager::JobProcessed(JsUtil::Job *const job, const bool succeeded)
  127. {
  128. processed = true;
  129. }
  130. JsUtil::Job *SingleJobManager::GetJob(bool)
  131. {
  132. return processed ? 0 : &job;
  133. }
  134. bool SingleJobManager::WasAddedToJobProcessor(JsUtil::Job *const job) const
  135. {
  136. return true;
  137. }
  138. // -------------------------------------------------------------------------------------------------------------------------
  139. // WaitableSingleJobManager
  140. // -------------------------------------------------------------------------------------------------------------------------
  141. WaitableSingleJobManager::WaitableSingleJobManager(JobProcessor *const processor, const bool isCritical)
  142. : WaitableJobManager(processor), job(isCritical), processed(false)
  143. {
  144. job.manager = this;
  145. }
  146. void WaitableSingleJobManager::AddJobToProcessor(const bool prioritize)
  147. {
  148. AutoOptionalCriticalSection lock(Processor()->GetCriticalSection());
  149. Processor()->AddJob(&job, prioritize);
  150. }
  151. void WaitableSingleJobManager::WaitForJobProcessed()
  152. {
  153. Processor()->PrioritizeJobAndWait(this, false);
  154. }
  155. void WaitableSingleJobManager::JobProcessed(JsUtil::Job *const job, const bool succeeded)
  156. {
  157. processed = true;
  158. }
  159. JsUtil::Job *WaitableSingleJobManager::GetJob(bool)
  160. {
  161. return processed ? 0 : &job;
  162. }
  163. bool WaitableSingleJobManager::WasAddedToJobProcessor(JsUtil::Job *const job) const
  164. {
  165. return true;
  166. }
  167. // -------------------------------------------------------------------------------------------------------------------------
  168. // JobProcessor
  169. // -------------------------------------------------------------------------------------------------------------------------
  170. JobProcessor::JobProcessor(const bool processesInBackground) : processesInBackground(processesInBackground), isClosed(false)
  171. {
  172. }
  173. bool JobProcessor::ProcessesInBackground() const
  174. {
  175. return processesInBackground;
  176. }
  177. bool JobProcessor::IsClosed() const
  178. {
  179. return isClosed;
  180. }
  181. CriticalSection *JobProcessor::GetCriticalSection()
  182. {
  183. #if ENABLE_BACKGROUND_JOB_PROCESSOR
  184. return processesInBackground ? static_cast<BackgroundJobProcessor *>(this)->GetCriticalSection() : 0;
  185. #else
  186. return 0;
  187. #endif
  188. }
  189. void JobProcessor::AddManager(JobManager *const manager)
  190. {
  191. Assert(manager);
  192. Assert(!isClosed);
  193. managers.LinkToEnd(manager);
  194. }
  195. bool JobProcessor::HasManager(JobManager *const manager) const
  196. {
  197. for (JobManager *curManager = managers.Head(); curManager != NULL; curManager = curManager->Next())
  198. {
  199. if (manager == curManager)
  200. {
  201. return true;
  202. }
  203. }
  204. return false;
  205. }
  206. template<class Fn>
  207. void JobProcessor::ForEachManager(Fn fn)
  208. {
  209. for (JobManager *curManager = managers.Head(); curManager != NULL; curManager = curManager->Next())
  210. {
  211. fn(curManager);
  212. }
  213. }
  214. void JobProcessor::PrioritizeManager(JobManager *const manager)
  215. {
  216. Assert(manager);
  217. Assert(!isClosed);
  218. managers.MoveToBeginning(manager);
  219. if (manager->numJobsAddedToProcessor == 0)
  220. {
  221. return;
  222. }
  223. // Move this manager's jobs to the beginning too. Find sequences of this manager's jobs backwards so that their relative
  224. // order remains intact after the sequences are moved.
  225. Job *const originalHead = jobs.Head();
  226. Job *lastJob = 0;
  227. for (Job *job = jobs.Tail(); job; job = job->Previous())
  228. {
  229. if (job->Manager() == manager)
  230. {
  231. if (!lastJob)
  232. lastJob = job;
  233. }
  234. else if (lastJob)
  235. {
  236. jobs.MoveSubsequenceToBeginning(job->Next(), lastJob);
  237. lastJob = 0;
  238. }
  239. if (job == originalHead)
  240. {
  241. break;
  242. }
  243. }
  244. if (lastJob)
  245. {
  246. jobs.MoveSubsequenceToBeginning(originalHead, lastJob);
  247. }
  248. }
  249. void JobProcessor::AddJob(Job *const job, const bool prioritize)
  250. {
  251. // This function is called from inside the lock
  252. Assert(job);
  253. Assert(managers.Contains(job->Manager()));
  254. Assert(!IsClosed());
  255. if (job->Manager()->numJobsAddedToProcessor + 1 == 0)
  256. Js::Throw::OutOfMemory(); // Overflow: job counts we use are int32's.
  257. ++job->Manager()->numJobsAddedToProcessor;
  258. if (prioritize)
  259. jobs.LinkToBeginning(job);
  260. else
  261. jobs.LinkToEnd(job);
  262. }
  263. bool JobProcessor::RemoveJob(Job *const job)
  264. {
  265. // This function is called from inside the lock
  266. Assert(job);
  267. Assert(managers.Contains(job->Manager()));
  268. Assert(!IsClosed());
  269. jobs.Unlink(job);
  270. Assert(job->Manager()->numJobsAddedToProcessor != 0);
  271. --job->Manager()->numJobsAddedToProcessor;
  272. return true;
  273. }
  274. void JobProcessor::JobProcessed(JobManager *const manager, Job *const job, const bool succeeded)
  275. {
  276. Assert(manager);
  277. Assert(job);
  278. Assert(manager == job->Manager());
  279. BEGIN_NO_EXCEPTION
  280. {
  281. manager->JobProcessed(job, succeeded);
  282. }
  283. END_NO_EXCEPTION;
  284. }
  285. void JobProcessor::LastJobProcessed(JobManager *const manager)
  286. {
  287. Assert(manager);
  288. BEGIN_NO_EXCEPTION
  289. {
  290. manager->LastJobProcessed();
  291. }
  292. END_NO_EXCEPTION;
  293. }
  294. void JobProcessor::Close()
  295. {
  296. isClosed = true;
  297. }
  298. // -------------------------------------------------------------------------------------------------------------------------
  299. // ForegroundJobProcessor
  300. // -------------------------------------------------------------------------------------------------------------------------
  301. ForegroundJobProcessor::ForegroundJobProcessor() : JobProcessor(false)
  302. {
  303. }
  304. void ForegroundJobProcessor::RemoveManager(JobManager *const manager)
  305. {
  306. Assert(manager);
  307. // Managers must remove themselves. Hence, Close does not remove managers. So, not asserting on !IsClosed().
  308. managers.Unlink(manager);
  309. if (manager->numJobsAddedToProcessor == 0)
  310. return;
  311. // Remove this manager's jobs from the queue
  312. Job *firstJob = 0;
  313. for (Job *job = jobs.Head(); job; job = job->Next())
  314. {
  315. if (job->Manager() == manager)
  316. {
  317. if (!firstJob)
  318. firstJob = job;
  319. }
  320. else if (firstJob)
  321. {
  322. jobs.UnlinkSubsequence(firstJob, job->Previous());
  323. for (Job *removedJob = firstJob; removedJob;)
  324. {
  325. Job *const next = removedJob->Next();
  326. Assert(!removedJob->IsCritical());
  327. JobProcessed(manager, removedJob, false); // the job may be deleted during this and should not be used afterwards
  328. Assert(manager->numJobsAddedToProcessor != 0);
  329. --manager->numJobsAddedToProcessor;
  330. removedJob = next;
  331. }
  332. firstJob = 0;
  333. }
  334. }
  335. if (firstJob)
  336. {
  337. jobs.UnlinkSubsequenceFromEnd(firstJob);
  338. for (Job *removedJob = firstJob; removedJob;)
  339. {
  340. Job *const next = removedJob->Next();
  341. Assert(!removedJob->IsCritical());
  342. JobProcessed(manager, removedJob, false); // the job may be deleted during this and should not be used afterwards
  343. Assert(manager->numJobsAddedToProcessor != 0);
  344. --manager->numJobsAddedToProcessor;
  345. removedJob = next;
  346. }
  347. }
  348. Assert(manager->numJobsAddedToProcessor == 0);
  349. LastJobProcessed(manager);
  350. }
  351. bool ForegroundJobProcessor::Process(Job *const job)
  352. {
  353. try
  354. {
  355. return job->Manager()->Process(job, 0);
  356. }
  357. catch (const Js::JavascriptException& err)
  358. {
  359. err.GetAndClear(); // discard exception object
  360. // Treat OOM or stack overflow to be a non-terminal failure. The foreground job processor processes jobs when the
  361. // jobs are prioritized, on the calling thread. The script would be active (at the time of this writing), so a
  362. // JavascriptExceptionObject would be thrown for OOM or stack overflow.
  363. }
  364. catch (Js::OperationAbortedException)
  365. {
  366. // This can happen for any reason a job needs to be aborted while executing
  367. }
  368. // Any of the above exceptions will result in the job failing. The return value of this function will cause the job
  369. // manager to get a JobProcessed call with succeeded = false, so that it can handle the failure appropriately.
  370. return false;
  371. }
  372. void ForegroundJobProcessor::Close()
  373. {
  374. if (IsClosed())
  375. return;
  376. for (Job *job = jobs.Head(); job;)
  377. {
  378. Job *const next = job->Next();
  379. JobManager *const manager = job->Manager();
  380. JobProcessed(
  381. manager,
  382. job,
  383. job->IsCritical() ? Process(job) : false); // the job may be deleted during this and should not be used afterwards
  384. Assert(manager->numJobsAddedToProcessor != 0);
  385. --manager->numJobsAddedToProcessor;
  386. if (manager->numJobsAddedToProcessor == 0)
  387. LastJobProcessed(manager); // the manager may be deleted during this and should not be used afterwards
  388. job = next;
  389. }
  390. jobs.Clear();
  391. JobProcessor::Close();
  392. }
  393. void ForegroundJobProcessor::AssociatePageAllocator(PageAllocator* const pageAllocator)
  394. {
  395. // Do nothing
  396. }
  397. void ForegroundJobProcessor::DissociatePageAllocator(PageAllocator* const pageAllocator)
  398. {
  399. // Do nothing
  400. }
  401. // Xplat-todo: revive BackgroundJobProcessor- we need this for the JIT
  402. #if ENABLE_BACKGROUND_JOB_PROCESSOR
  403. // -------------------------------------------------------------------------------------------------------------------------
  404. // BackgroundJobProcessor
  405. // -------------------------------------------------------------------------------------------------------------------------
  406. void BackgroundJobProcessor::InitializeThreadCount()
  407. {
  408. if (CONFIG_FLAG(ForceMaxJitThreadCount))
  409. {
  410. this->maxThreadCount = CONFIG_FLAG(MaxJitThreadCount);
  411. }
  412. else if (AutoSystemInfo::Data.IsLowMemoryProcess())
  413. {
  414. // In a low-memory scenario, don't spin up multiple threads, regardless of how many cores we have.
  415. this->maxThreadCount = 1;
  416. }
  417. else
  418. {
  419. int processorCount = AutoSystemInfo::Data.GetNumberOfPhysicalProcessors();
  420. //There is 2 threads already in play, one UI (main) thread and a GC thread. So subtract 2 from processorCount to account for the same.
  421. this->maxThreadCount = max(1, min(processorCount - 2, CONFIG_FLAG(MaxJitThreadCount)));
  422. }
  423. }
  424. void BackgroundJobProcessor::InitializeParallelThreadData(AllocationPolicyManager* policyManager, bool disableParallelThreads)
  425. {
  426. if (!disableParallelThreads)
  427. {
  428. InitializeThreadCount();
  429. }
  430. else
  431. {
  432. this->maxThreadCount = 1;
  433. }
  434. Assert(this->maxThreadCount >= 1);
  435. this->parallelThreadData = HeapNewArrayZ(ParallelThreadData*, this->maxThreadCount);
  436. for (uint i = 0; i < this->maxThreadCount; i++)
  437. {
  438. this->parallelThreadData[i] = HeapNewNoThrow(ParallelThreadData, policyManager);
  439. if (this->parallelThreadData[i] == nullptr)
  440. {
  441. if (i == 0)
  442. {
  443. HeapDeleteArray(this->maxThreadCount, this->parallelThreadData);
  444. Js::Throw::OutOfMemory();
  445. }
  446. // At least one thread is created, continue
  447. break;
  448. }
  449. this->parallelThreadData[i]->processor = this;
  450. // Make sure to create the thread suspended so the thread handle can be assigned before the thread starts running
  451. this->parallelThreadData[i]->threadHandle = reinterpret_cast<HANDLE>(PlatformAgnostic::Thread::Create(0, &StaticThreadProc, this->parallelThreadData[i], PlatformAgnostic::Thread::ThreadInitCreateSuspended));
  452. if (!this->parallelThreadData[i]->threadHandle)
  453. {
  454. HeapDelete(parallelThreadData[i]);
  455. parallelThreadData[i] = nullptr;
  456. if (i == 0)
  457. {
  458. Js::Throw::OutOfMemory();
  459. }
  460. // At least one thread is created, continue
  461. break;
  462. }
  463. if (ResumeThread(this->parallelThreadData[i]->threadHandle) == static_cast<DWORD>(-1))
  464. {
  465. CloseHandle(this->parallelThreadData[i]->threadHandle);
  466. HeapDelete(parallelThreadData[i]);
  467. this->parallelThreadData[i] = nullptr;
  468. if (i == 0)
  469. {
  470. Js::Throw::OutOfMemory();
  471. }
  472. // At least one thread is created, continue
  473. break;
  474. }
  475. this->threadCount++;
  476. // Wait for the thread to fully start. This is necessary because Close may be called before the thread starts and if
  477. // Close is called while holding the loader lock during DLL_THREAD_DETACH, the thread may be stuck waiting for the
  478. // loader lock for DLL_THREAD_ATTACH to start up, and Close would then end up waiting forever, causing a deadlock.
  479. WaitWithThreadForThreadStartedOrClosingEvent(this->parallelThreadData[i]);
  480. this->parallelThreadData[i]->threadStartedOrClosing.Reset(); // after this, the event will be used to wait for the thread to close
  481. #if DBG_DUMP
  482. if (i < (sizeof(DebugThreadNames) / sizeof(DebugThreadNames[i])))
  483. {
  484. this->parallelThreadData[i]->backgroundPageAllocator.debugName = DebugThreadNames[i];
  485. }
  486. else
  487. {
  488. this->parallelThreadData[i]->backgroundPageAllocator.debugName = _u("BackgroundJobProcessor thread");
  489. }
  490. #endif
  491. }
  492. Assert(this->threadCount >= 1);
  493. }
  494. void BackgroundJobProcessor::InitializeParallelThreadDataForThreadServiceCallBack(AllocationPolicyManager* policyManager)
  495. {
  496. //thread is provided by service callback, no need to create thread here. Currently only one thread in service callback supported.
  497. this->maxThreadCount = 1;
  498. this->parallelThreadData = HeapNewArrayZ(ParallelThreadData *, this->maxThreadCount);
  499. this->parallelThreadData[0] = HeapNewNoThrow(ParallelThreadData, policyManager);
  500. if (this->parallelThreadData[0] == nullptr)
  501. {
  502. HeapDeleteArray(this->maxThreadCount, this->parallelThreadData);
  503. Js::Throw::OutOfMemory();
  504. }
  505. this->parallelThreadData[0]->processor = this;
  506. this->parallelThreadData[0]->isWaitingForJobs = true;
  507. #if DBG_DUMP
  508. this->parallelThreadData[0]->backgroundPageAllocator.debugName = _u("BackgroundJobProcessor");
  509. #endif
  510. this->threadCount = 1;
  511. return;
  512. }
  513. BackgroundJobProcessor::BackgroundJobProcessor(AllocationPolicyManager* policyManager, JsUtil::ThreadService *threadService, bool disableParallelThreads)
  514. : JobProcessor(true),
  515. jobReady(true),
  516. wakeAllBackgroundThreads(false),
  517. numJobs(0),
  518. threadId(GetCurrentThreadContextId()),
  519. threadService(threadService),
  520. threadCount(0),
  521. maxThreadCount(0)
  522. {
  523. if (!threadService->HasCallback())
  524. {
  525. // We don't have a thread service, so create a dedicated thread to handle background jobs.
  526. InitializeParallelThreadData(policyManager, disableParallelThreads);
  527. }
  528. else
  529. {
  530. InitializeParallelThreadDataForThreadServiceCallBack(policyManager);
  531. }
  532. }
  533. BackgroundJobProcessor::~BackgroundJobProcessor()
  534. {
  535. // This should appear to be called from the same thread from which this instance was created
  536. Assert(IsClosed());
  537. if (parallelThreadData)
  538. {
  539. for (unsigned int i = 0; i < this->threadCount; i++)
  540. {
  541. HeapDelete(parallelThreadData[i]);
  542. }
  543. HeapDeleteArray(this->maxThreadCount, parallelThreadData);
  544. }
  545. }
  546. void BackgroundJobProcessor::WaitWithAllThreadsForThreadStartedOrClosingEvent()
  547. {
  548. bool continueWaiting = true;
  549. this->IterateBackgroundThreads([&](ParallelThreadData *threadData)
  550. {
  551. if (continueWaiting)
  552. {
  553. continueWaiting = WaitWithThreadForThreadStartedOrClosingEvent(threadData);
  554. }
  555. else
  556. {
  557. //one of the thread is terminated, its sure shutdown scenario. Just reset the waitingForjobs.
  558. threadData->isWaitingForJobs = false;
  559. }
  560. return false;
  561. });
  562. }
  563. template<class Fn>
  564. void BackgroundJobProcessor::ForEachManager(Fn fn)
  565. {
  566. AutoCriticalSection lock(&criticalSection);
  567. JobProcessor::ForEachManager(fn);
  568. }
  569. //This function waits on two events jobReady or wakeAllBackgroundThreads
  570. //It first waits for 1sec and if it times out it will decommit the allocator and wait infinitely.
  571. bool BackgroundJobProcessor::WaitForJobReadyOrShutdown(ParallelThreadData *threadData)
  572. {
  573. const HANDLE handles[] = { jobReady.Handle(), wakeAllBackgroundThreads.Handle() };
  574. //Wait for 1 sec on jobReady and shutdownBackgroundThread events.
  575. unsigned int result = WaitForMultipleObjectsEx(_countof(handles), handles, false, 1000, false);
  576. while (result == WAIT_TIMEOUT)
  577. {
  578. if (threadData->CanDecommit())
  579. {
  580. // If its 1sec time out decommit and wait for INFINITE
  581. threadData->backgroundPageAllocator.DecommitNow();
  582. this->ForEachManager([&](JobManager *manager){
  583. manager->OnDecommit(threadData);
  584. });
  585. result = WaitForMultipleObjectsEx(_countof(handles), handles, false, INFINITE, false);
  586. }
  587. else
  588. {
  589. result = WaitForMultipleObjectsEx(_countof(handles), handles, false, 1000, false);
  590. }
  591. }
  592. if (!(result == WAIT_OBJECT_0 || result == WAIT_OBJECT_0 + 1))
  593. {
  594. Js::Throw::FatalInternalError();
  595. }
  596. return result == WAIT_OBJECT_0;
  597. }
  598. bool BackgroundJobProcessor::WaitWithThreadForThreadStartedOrClosingEvent(ParallelThreadData *parallelThreadData, const unsigned int milliseconds)
  599. {
  600. return WaitWithThread(parallelThreadData, parallelThreadData->threadStartedOrClosing, milliseconds);
  601. }
  602. bool BackgroundJobProcessor::WaitWithThread(ParallelThreadData *parallelThreadData, const Event &e, const unsigned int milliseconds)
  603. {
  604. const HANDLE handles[] = { e.Handle(), parallelThreadData->threadHandle };
  605. // If we have a thread service, then only wait on the event, not the actual thread handle.
  606. DWORD handleCount = 2;
  607. if (threadService->HasCallback())
  608. {
  609. handleCount = 1;
  610. }
  611. const unsigned int result = WaitForMultipleObjectsEx(handleCount, handles, false, milliseconds, false);
  612. if (!(result == WAIT_OBJECT_0 || result == WAIT_OBJECT_0 + 1 || (result == WAIT_TIMEOUT && milliseconds != INFINITE)))
  613. {
  614. Js::Throw::FatalInternalError();
  615. }
  616. if (result == WAIT_OBJECT_0 + 1)
  617. {
  618. // Apparently, sometimes the thread dies while waiting for an event. It should only be during process shutdown but
  619. // we can't know because DLL_PROCESS_DETACH may not have been called yet, which is bizarre. It seems unclear why
  620. // this happens and this could cause unpredictable behavior since the behavior of this object is undefined if the
  621. // thread is killed arbitrarily, or if there are incoming calls after Close. In any case, uses of this function have
  622. // been ported from BackgroundCodeGenThread. For now, we assume that Close will be called eventually and set the
  623. // state to what it should be before Close is called.
  624. parallelThreadData->isWaitingForJobs = false;
  625. }
  626. return result == WAIT_OBJECT_0;
  627. }
  628. void BackgroundJobProcessor::AddManager(JobManager *const manager)
  629. {
  630. Assert(manager);
  631. IterateBackgroundThreads([&manager](ParallelThreadData *threadData){
  632. manager->ProcessorThreadSpecificCallBack(threadData->GetPageAllocator());
  633. return false;
  634. });
  635. AutoCriticalSection lock(&criticalSection);
  636. Assert(!IsClosed());
  637. JobProcessor::AddManager(manager);
  638. IndicateNewJob();
  639. }
  640. void BackgroundJobProcessor::IndicateNewJob()
  641. {
  642. Assert(criticalSection.IsLocked());
  643. if(NumberOfThreadsWaitingForJobs ())
  644. {
  645. if (threadService->HasCallback())
  646. {
  647. Assert(this->threadCount == 1);
  648. this->parallelThreadData[0]->isWaitingForJobs = false;
  649. // Reset the thread event, so we can wait for it on shutdown.
  650. this->parallelThreadData[0]->threadStartedOrClosing.Reset();
  651. // Submit a request to the thread service.
  652. bool success = threadService->Invoke(ThreadServiceCallback, this);
  653. if (!success)
  654. {
  655. // The thread service denied our request.
  656. // Leave the job in the queue. If it's needed, it will be processed
  657. // in-thread during PrioritizeJob. Or alternatively, if a subsequent
  658. // thread service request succeeds, this job will be processed then.
  659. this->parallelThreadData[0]->isWaitingForJobs = true;
  660. }
  661. }
  662. else
  663. {
  664. // Signal the background thread to wake up and process jobs.
  665. jobReady.Set();
  666. }
  667. }
  668. }
  669. Job * BackgroundJobProcessor::GetCurrentJobOfManager(JobManager *const manager)
  670. {
  671. Assert(criticalSection.IsLocked());
  672. Job *currentJob = nullptr;
  673. this->IterateBackgroundThreads([&](ParallelThreadData* threadData)
  674. {
  675. if (!threadData->currentJob)
  676. {
  677. return false;
  678. }
  679. if (threadData->currentJob->Manager() != manager)
  680. {
  681. return false;
  682. }
  683. currentJob = threadData->currentJob;
  684. return true;
  685. }
  686. );
  687. return currentJob;
  688. }
  689. ParallelThreadData * BackgroundJobProcessor::GetThreadDataFromCurrentJob(Job* job)
  690. {
  691. Assert(criticalSection.IsLocked());
  692. ParallelThreadData *currentThreadData = nullptr;
  693. this->IterateBackgroundThreads([&](ParallelThreadData* threadData)
  694. {
  695. if (!threadData->currentJob)
  696. {
  697. return false;
  698. }
  699. if (threadData->currentJob != job)
  700. {
  701. return false;
  702. }
  703. currentThreadData = threadData;
  704. return true;
  705. }
  706. );
  707. return currentThreadData;
  708. }
  709. void BackgroundJobProcessor::RemoveManager(JobManager *const manager)
  710. {
  711. Assert(manager);
  712. ParallelThreadData *threadDataProcessingCurrentJob = nullptr;
  713. {
  714. AutoCriticalSection lock(&criticalSection);
  715. // Managers must remove themselves. Hence, Close does not remove managers. So, not asserting on !IsClosed().
  716. managers.Unlink(manager);
  717. if(manager->numJobsAddedToProcessor == 0)
  718. {
  719. Assert(!GetCurrentJobOfManager(manager));
  720. return;
  721. }
  722. // Remove this manager's jobs from the queue
  723. Job *firstJob = 0;
  724. for(Job *job = jobs.Head(); job; job = job->Next())
  725. {
  726. if(job->Manager() == manager)
  727. {
  728. if(!firstJob)
  729. firstJob = job;
  730. }
  731. else if(firstJob)
  732. {
  733. jobs.UnlinkSubsequence(firstJob, job->Previous());
  734. for(Job *removedJob = firstJob; removedJob;)
  735. {
  736. Job *const next = removedJob->Next();
  737. Assert(!removedJob->IsCritical());
  738. Assert(numJobs != 0);
  739. --numJobs;
  740. JobProcessed(manager, removedJob, false); // the job may be deleted during this and should not be used afterwards
  741. Assert(manager->numJobsAddedToProcessor != 0);
  742. --manager->numJobsAddedToProcessor;
  743. if(manager->isWaitable)
  744. {
  745. WaitableJobManager *const waitableManager = static_cast<WaitableJobManager *>(manager);
  746. if(waitableManager->jobBeingWaitedUpon == removedJob)
  747. {
  748. waitableManager->jobBeingWaitedUponProcessed.Set();
  749. waitableManager->jobBeingWaitedUpon = 0;
  750. }
  751. }
  752. removedJob = next;
  753. }
  754. firstJob = 0;
  755. }
  756. }
  757. if(firstJob)
  758. {
  759. jobs.UnlinkSubsequenceFromEnd(firstJob);
  760. for(Job *removedJob = firstJob; removedJob;)
  761. {
  762. Job *const next = removedJob->Next();
  763. Assert(!removedJob->IsCritical());
  764. Assert(numJobs != 0);
  765. --numJobs;
  766. JobProcessed(manager, removedJob, false); // the job may be deleted during this and should not be used afterwards
  767. Assert(manager->numJobsAddedToProcessor != 0);
  768. --manager->numJobsAddedToProcessor;
  769. if(manager->isWaitable)
  770. {
  771. WaitableJobManager *const waitableManager = static_cast<WaitableJobManager *>(manager);
  772. if(waitableManager->jobBeingWaitedUpon == removedJob)
  773. {
  774. waitableManager->jobBeingWaitedUponProcessed.Set();
  775. waitableManager->jobBeingWaitedUpon = 0;
  776. }
  777. }
  778. removedJob = next;
  779. }
  780. }
  781. if(manager->numJobsAddedToProcessor == 0)
  782. {
  783. LastJobProcessed(manager);
  784. return;
  785. }
  786. Assert(manager->numJobsAddedToProcessor >= 1);
  787. Assert(manager->isWaitable);
  788. Assert(GetCurrentJobOfManager(manager));
  789. }
  790. //Wait for all the on going jobs to complete.
  791. criticalSection.Enter();
  792. while (true)
  793. {
  794. Job *job = GetCurrentJobOfManager(manager);
  795. if (!job)
  796. {
  797. break;
  798. }
  799. WaitableJobManager * const waitableManager = static_cast<WaitableJobManager *>(manager);
  800. Assert(!waitableManager->jobBeingWaitedUpon);
  801. waitableManager->jobBeingWaitedUpon = job;
  802. waitableManager->jobBeingWaitedUponProcessed.Reset();
  803. threadDataProcessingCurrentJob = GetThreadDataFromCurrentJob(waitableManager->jobBeingWaitedUpon);
  804. criticalSection.Leave();
  805. WaitWithThread(threadDataProcessingCurrentJob, waitableManager->jobBeingWaitedUponProcessed);
  806. criticalSection.Enter();
  807. waitableManager->jobBeingWaitedUpon = 0;
  808. }
  809. criticalSection.Leave();
  810. }
  811. void BackgroundJobProcessor::AddJob(Job *const job, const bool prioritize)
  812. {
  813. // This function is called from inside the lock
  814. Assert(job);
  815. Assert(managers.Contains(job->Manager()));
  816. Assert(!IsClosed());
  817. if(numJobs + 1 == 0)
  818. Js::Throw::OutOfMemory(); // Overflow: job counts we use are int32's.
  819. ++numJobs;
  820. __super::AddJob(job, prioritize);
  821. IndicateNewJob();
  822. }
  823. bool BackgroundJobProcessor::RemoveJob(Job *const job)
  824. {
  825. // This function is called from inside the lock
  826. Assert(job);
  827. Assert(managers.Contains(job->Manager()));
  828. Assert(!IsClosed());
  829. if (IsBeingProcessed(job))
  830. {
  831. return false;
  832. }
  833. return __super::RemoveJob(job);
  834. }
  835. bool BackgroundJobProcessor::Process(Job *const job, ParallelThreadData *threadData)
  836. {
  837. try
  838. {
  839. AUTO_HANDLED_EXCEPTION_TYPE(static_cast<ExceptionType>(ExceptionType_OutOfMemory | ExceptionType_StackOverflow));
  840. return job->Manager()->Process(job, threadData);
  841. }
  842. catch(Js::OutOfMemoryException)
  843. {
  844. // Treat OOM to be a non-terminal failure
  845. #if ENABLE_DEBUG_CONFIG_OPTIONS
  846. job->failureReason = Job::FailureReason::OOM;
  847. #endif
  848. }
  849. catch(Js::StackOverflowException)
  850. {
  851. // Treat stack overflow to be a non-terminal failure
  852. #if ENABLE_DEBUG_CONFIG_OPTIONS
  853. job->failureReason = Job::FailureReason::StackOverflow;
  854. #endif
  855. }
  856. catch(Js::OperationAbortedException)
  857. {
  858. // This can happen for any reason a job needs to be aborted while executing, like for instance, if the script
  859. // context is closed while the job is being processed in the background
  860. #if ENABLE_DEBUG_CONFIG_OPTIONS
  861. job->failureReason = Job::FailureReason::Aborted;
  862. #endif
  863. }
  864. // Since the background job processor processes jobs on a background thread, out-of-memory and stack overflow need to be
  865. // caught here. Script would not be active in the background thread, so (at the time of this writing) a
  866. // JavascriptException would never be thrown and instead the corresponding exceptions caught above would be thrown.
  867. // Any of the above exceptions will result in the job failing. The return value of this function will cause the job
  868. // manager to get a JobProcessed call with succeeded = false, so that it can handle the failure appropriately.
  869. return false;
  870. }
  871. void BackgroundJobProcessor::Run(ParallelThreadData* threadData)
  872. {
  873. EDGE_ETW_INTERNAL(EventWriteJSCRIPT_NATIVECODEGEN_START(this, 0));
  874. ArenaAllocator threadArena(_u("ThreadArena"), threadData->GetPageAllocator(), Js::Throw::OutOfMemory);
  875. threadData->threadArena = &threadArena;
  876. {
  877. // Make sure we take decommit action before the threadArena is torn down, in case the
  878. // thread context goes away and the loop exits.
  879. struct AutoDecommit
  880. {
  881. AutoDecommit(JobProcessor *proc, ParallelThreadData *data) : processor(proc), threadData(data) {}
  882. ~AutoDecommit()
  883. {
  884. processor->ForEachManager([this](JobManager *manager){
  885. manager->OnDecommit(this->threadData);
  886. });
  887. }
  888. ParallelThreadData *threadData;
  889. JobProcessor *processor;
  890. } autoDecommit(this, threadData);
  891. criticalSection.Enter();
  892. while (!IsClosed() || (jobs.Head() && jobs.Head()->IsCritical()))
  893. {
  894. Job *job = jobs.UnlinkFromBeginning();
  895. if(!job)
  896. {
  897. // No jobs in queue, wait for one
  898. Assert(!IsClosed());
  899. Assert(!threadData->isWaitingForJobs);
  900. threadData->isWaitingForJobs = true;
  901. criticalSection.Leave();
  902. EDGE_ETW_INTERNAL(EventWriteJSCRIPT_NATIVECODEGEN_STOP(this, 0));
  903. if (threadService->HasCallback())
  904. {
  905. // We have a thread service, so simply return the thread back now.
  906. // When new jobs are submitted, we will be called to process again.
  907. return;
  908. }
  909. WaitForJobReadyOrShutdown(threadData);
  910. EDGE_ETW_INTERNAL(EventWriteJSCRIPT_NATIVECODEGEN_START(this, 0));
  911. criticalSection.Enter();
  912. threadData->isWaitingForJobs = false;
  913. continue;
  914. }
  915. Assert(numJobs != 0);
  916. --numJobs;
  917. threadData->currentJob = job;
  918. criticalSection.Leave();
  919. const bool succeeded = Process(job, threadData);
  920. criticalSection.Enter();
  921. threadData->currentJob = 0;
  922. JobManager *const manager = job->Manager();
  923. JobProcessed(manager, job, succeeded); // the job may be deleted during this and should not be used afterwards
  924. Assert(manager->numJobsAddedToProcessor != 0);
  925. --manager->numJobsAddedToProcessor;
  926. if(manager->isWaitable)
  927. {
  928. WaitableJobManager *const waitableManager = static_cast<WaitableJobManager *>(manager);
  929. Assert(!(waitableManager->jobBeingWaitedUpon && waitableManager->isWaitingForQueuedJobs));
  930. if(waitableManager->jobBeingWaitedUpon == job)
  931. {
  932. waitableManager->jobBeingWaitedUpon = 0;
  933. waitableManager->jobBeingWaitedUponProcessed.Set();
  934. }
  935. else if(waitableManager->isWaitingForQueuedJobs && manager->numJobsAddedToProcessor == 0)
  936. {
  937. waitableManager->isWaitingForQueuedJobs = false;
  938. waitableManager->queuedJobsProcessed.Set();
  939. }
  940. }
  941. if(manager->numJobsAddedToProcessor == 0)
  942. LastJobProcessed(manager); // the manager may be deleted during this and should not be used afterwards
  943. }
  944. criticalSection.Leave();
  945. EDGE_ETW_INTERNAL(EventWriteJSCRIPT_NATIVECODEGEN_STOP(this, 0));
  946. }
  947. }
  948. void BackgroundJobProcessor::Close()
  949. {
  950. // The contract for Close is that from the time it's called, job managers and jobs may no longer be added to the job
  951. // processor. If there is potential background work that needs to be done after Close, it must be done directly on the
  952. // foreground thread.
  953. if(IsClosed())
  954. return;
  955. bool waitForThread = true;
  956. uint threadsWaitingForJobs = 0;
  957. {
  958. AutoCriticalSection lock(&criticalSection);
  959. if(IsClosed())
  960. return;
  961. Job *nextJob = jobs.Head();
  962. while(nextJob)
  963. {
  964. Job *const job = nextJob;
  965. nextJob = job->Next();
  966. if(job->IsCritical())
  967. {
  968. // Critical jobs need to be left in the queue. After this instance is flagged as closed, the background
  969. // thread will continue processing critical jobs, for which this function will wait before returning.
  970. continue;
  971. }
  972. jobs.Unlink(job);
  973. Assert(numJobs != 0);
  974. --numJobs;
  975. JobManager *const manager = job->Manager();
  976. JobProcessed(manager, job, false); // the job may be deleted during this and should not be used afterwards
  977. Assert(manager->numJobsAddedToProcessor != 0);
  978. --manager->numJobsAddedToProcessor;
  979. if(manager->isWaitable)
  980. {
  981. WaitableJobManager *const waitableManager = static_cast<WaitableJobManager *>(manager);
  982. if(waitableManager->jobBeingWaitedUpon == job)
  983. {
  984. waitableManager->jobBeingWaitedUponProcessed.Set();
  985. waitableManager->jobBeingWaitedUpon = 0;
  986. }
  987. }
  988. if(manager->numJobsAddedToProcessor == 0)
  989. {
  990. Assert(!GetCurrentJobOfManager(manager));
  991. LastJobProcessed(manager); // the manager may be deleted during this and should not be used afterwards
  992. }
  993. }
  994. // Managers will remove themselves, so not removing managers here
  995. JobProcessor::Close();
  996. if (threadService->HasCallback())
  997. {
  998. Assert(this->threadCount == 1);
  999. // If there are no outstanding jobs, then we don't currently have a thread, so there's no reason to wait for it.
  1000. waitForThread = !this->parallelThreadData[0]->isWaitingForJobs;
  1001. }
  1002. else
  1003. {
  1004. threadsWaitingForJobs = NumberOfThreadsWaitingForJobs ();
  1005. }
  1006. }
  1007. if (threadsWaitingForJobs)
  1008. {
  1009. //There is no reset for this. It will be signaled until all the threads get out of their hibernation.
  1010. wakeAllBackgroundThreads.Set();
  1011. }
  1012. // We cannot wait for the background thread to terminate because this function may be called from DLL_THREAD_DETACH, and
  1013. // waiting for the background thread would then deadlock because the background thread would also be blocked from
  1014. // detaching. Instead, we just wait for this event to be signaled, which indicates that the thread will promptly end
  1015. // naturally. The caller should wait as necessary for the thread to terminate.
  1016. if (waitForThread)
  1017. {
  1018. WaitWithAllThreadsForThreadStartedOrClosingEvent();
  1019. }
  1020. #if DBG
  1021. if (!threadService->HasCallback())
  1022. {
  1023. AutoCriticalSection lock(&criticalSection);
  1024. Assert(!NumberOfThreadsWaitingForJobs());
  1025. this->IterateBackgroundThreads([](ParallelThreadData *threadData)
  1026. {
  1027. threadData->backgroundPageAllocator.ClearConcurrentThreadId();
  1028. return false;
  1029. }
  1030. );
  1031. }
  1032. #endif
  1033. if (threadService->HasCallback())
  1034. {
  1035. Assert(this->threadCount == 1 && (this->parallelThreadData[0]->threadHandle == 0));
  1036. return;
  1037. }
  1038. else
  1039. {
  1040. // Close all the handles
  1041. this->IterateBackgroundThreads([&](ParallelThreadData *threadData)
  1042. {
  1043. CloseHandle(threadData->threadHandle);
  1044. threadData->threadHandle = 0;
  1045. return false;
  1046. });
  1047. }
  1048. }
  1049. unsigned int WINAPI BackgroundJobProcessor::StaticThreadProc(void *lpParam)
  1050. {
  1051. Assert(lpParam);
  1052. #ifdef TARGET_64
  1053. #ifdef RECYCLER_WRITE_BARRIER
  1054. Memory::RecyclerWriteBarrierManager::OnThreadInit();
  1055. #endif
  1056. #endif
  1057. #if !defined(_UCRT)
  1058. HMODULE dllHandle = NULL;
  1059. if (!GetModuleHandleEx(0, AutoSystemInfo::GetJscriptDllFileName(), &dllHandle))
  1060. {
  1061. dllHandle = NULL;
  1062. }
  1063. #endif
  1064. ParallelThreadData * threadData = static_cast<ParallelThreadData *>(lpParam);
  1065. BackgroundJobProcessor *const processor = threadData->processor;
  1066. // Indicate to the constructor that the thread has fully started.
  1067. threadData->threadStartedOrClosing.Set();
  1068. #if DBG
  1069. threadData->backgroundPageAllocator.SetConcurrentThreadId(GetCurrentThreadId());
  1070. #endif
  1071. #ifdef DISABLE_SEH
  1072. processor->Run(threadData);
  1073. #else
  1074. __try
  1075. {
  1076. processor->Run(threadData);
  1077. }
  1078. __except(ExceptFilter(GetExceptionInformation()))
  1079. {
  1080. Assert(false);
  1081. }
  1082. #endif
  1083. // Indicate to Close that the thread is about to exit. This has to be done before CoUninitialize because CoUninitialize
  1084. // may require the loader lock and if Close was called while holding the loader lock during DLL_THREAD_DETACH, it could
  1085. // end up waiting forever, causing a deadlock.
  1086. threadData->threadStartedOrClosing.Set();
  1087. #if !defined(_UCRT)
  1088. if (dllHandle)
  1089. {
  1090. FreeLibraryAndExitThread(dllHandle, 0);
  1091. }
  1092. else
  1093. #endif
  1094. {
  1095. return 0;
  1096. }
  1097. }
  1098. #ifndef DISABLE_SEH
  1099. int BackgroundJobProcessor::ExceptFilter(LPEXCEPTION_POINTERS pEP)
  1100. {
  1101. #if DBG && defined(_WIN32)
  1102. // Assert exception code
  1103. if (pEP->ExceptionRecord->ExceptionCode == STATUS_ASSERTION_FAILURE)
  1104. {
  1105. return EXCEPTION_CONTINUE_SEARCH;
  1106. }
  1107. #endif
  1108. #ifdef GENERATE_DUMP
  1109. if (Js::Configuration::Global.flags.IsEnabled(Js::DumpOnCrashFlag))
  1110. {
  1111. Js::Throw::GenerateDump(pEP, Js::Configuration::Global.flags.DumpOnCrash);
  1112. }
  1113. #endif
  1114. #if DBG && _M_IX86
  1115. int callerEBP = *((int*)pEP->ContextRecord->Ebp);
  1116. Output::Print(_u("BackgroundJobProcessor: Uncaught exception: EIP: 0x%X ExceptionCode: 0x%X EBP: 0x%X ReturnAddress: 0x%X ReturnAddress2: 0x%X\n"),
  1117. pEP->ExceptionRecord->ExceptionAddress, pEP->ExceptionRecord->ExceptionCode, pEP->ContextRecord->Eip,
  1118. pEP->ContextRecord->Ebp, *((int*)pEP->ContextRecord->Ebp + 1), *((int*) callerEBP + 1));
  1119. #endif
  1120. Output::Flush();
  1121. return EXCEPTION_CONTINUE_SEARCH;
  1122. }
  1123. #endif
  1124. void BackgroundJobProcessor::ThreadServiceCallback(void * callbackData)
  1125. {
  1126. BackgroundJobProcessor * jobProcessor = (BackgroundJobProcessor *)callbackData;
  1127. Assert(jobProcessor->threadCount == 1);
  1128. #if DBG
  1129. jobProcessor->parallelThreadData[0]->backgroundPageAllocator.SetConcurrentThreadId(GetCurrentThreadId());
  1130. #endif
  1131. jobProcessor->Run(jobProcessor->parallelThreadData[0]);
  1132. #if DBG
  1133. jobProcessor->parallelThreadData[0]->backgroundPageAllocator.ClearConcurrentThreadId();
  1134. #endif
  1135. // Set the thread event, in case we are waiting for it on shutdown.
  1136. jobProcessor->parallelThreadData[0]->threadStartedOrClosing.Set();
  1137. }
  1138. bool BackgroundJobProcessor::AreAllThreadsWaitingForJobs()
  1139. {
  1140. Assert(criticalSection.IsLocked());
  1141. bool isAnyThreadNotWaitingForJobs = false;
  1142. this->IterateBackgroundThreads([&](ParallelThreadData *parallelThreadData)
  1143. {
  1144. if (parallelThreadData->isWaitingForJobs)
  1145. {
  1146. return false;
  1147. }
  1148. // At least one thread was not waiting for jobs.
  1149. isAnyThreadNotWaitingForJobs = true;
  1150. return true;
  1151. });
  1152. return !isAnyThreadNotWaitingForJobs;
  1153. }
  1154. uint BackgroundJobProcessor::NumberOfThreadsWaitingForJobs ()
  1155. {
  1156. Assert(criticalSection.IsLocked());
  1157. uint countOfThreadsWaitingForJobs = 0;
  1158. this->IterateBackgroundThreads([&](ParallelThreadData *parallelThreadData)
  1159. {
  1160. if (parallelThreadData->isWaitingForJobs)
  1161. {
  1162. // At least one thread is waiting for jobs.
  1163. countOfThreadsWaitingForJobs++;
  1164. }
  1165. return false;
  1166. });
  1167. return countOfThreadsWaitingForJobs;
  1168. }
  1169. bool BackgroundJobProcessor::IsBeingProcessed(Job* job)
  1170. {
  1171. Assert(criticalSection.IsLocked());
  1172. bool isBeingProcessed = false;
  1173. this->IterateBackgroundThreads([&](ParallelThreadData *parallelThreadData)
  1174. {
  1175. if (parallelThreadData->currentJob == job)
  1176. {
  1177. isBeingProcessed = true;
  1178. return true;
  1179. }
  1180. return false;
  1181. });
  1182. return isBeingProcessed;
  1183. }
  1184. void BackgroundJobProcessor::AssociatePageAllocator(PageAllocator* const pageAllocator)
  1185. {
  1186. #if DBG
  1187. pageAllocator->SetConcurrentThreadId(::GetCurrentThreadId());
  1188. #endif
  1189. }
  1190. void BackgroundJobProcessor::DissociatePageAllocator(PageAllocator* const pageAllocator)
  1191. {
  1192. // This function is called from the foreground thread
  1193. #if DBG
  1194. // Assert that the dissociation is happening in the same thread that created the background job processor
  1195. Assert(GetCurrentThreadContextId() == this->threadId);
  1196. pageAllocator->ClearConcurrentThreadId();
  1197. #endif
  1198. }
  1199. #if DBG_DUMP
  1200. //Just for debugging purpose
  1201. char16 const * const BackgroundJobProcessor::DebugThreadNames[16] = {
  1202. _u("BackgroundJobProcessor thread 1"),
  1203. _u("BackgroundJobProcessor thread 2"),
  1204. _u("BackgroundJobProcessor thread 3"),
  1205. _u("BackgroundJobProcessor thread 4"),
  1206. _u("BackgroundJobProcessor thread 5"),
  1207. _u("BackgroundJobProcessor thread 6"),
  1208. _u("BackgroundJobProcessor thread 7"),
  1209. _u("BackgroundJobProcessor thread 8"),
  1210. _u("BackgroundJobProcessor thread 9"),
  1211. _u("BackgroundJobProcessor thread 10"),
  1212. _u("BackgroundJobProcessor thread 11"),
  1213. _u("BackgroundJobProcessor thread 12"),
  1214. _u("BackgroundJobProcessor thread 13"),
  1215. _u("BackgroundJobProcessor thread 14"),
  1216. _u("BackgroundJobProcessor thread 15"),
  1217. _u("BackgroundJobProcessor thread 16") };
  1218. #endif
  1219. #endif // ENABLE_BACKGROUND_JOB_PROCESSOR
  1220. }