BGParseManager.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679
  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 "ParserPch.h"
  6. #include "Memory/AutoPtr.h"
  7. #include "Common/Event.h"
  8. #include "Base/ThreadContextTlsEntry.h"
  9. #include "Base/ThreadBoundThreadContextManager.h"
  10. #include "Base/Utf8SourceInfo.h"
  11. #include "BGParseManager.h"
  12. #include "Base/ScriptContext.h"
  13. #include "ByteCodeSerializer.h"
  14. #define BGPARSE_FLAGS (fscrGlobalCode | fscrWillDeferFncParse | fscrCanDeferFncParse | fscrCreateParserState)
  15. // Global, process singleton
  16. BGParseManager* BGParseManager::s_BGParseManager = nullptr;
  17. DWORD BGParseManager::s_lastCookie = 0;
  18. DWORD BGParseManager::s_completed = 0;
  19. DWORD BGParseManager::s_failed = 0;
  20. CriticalSection BGParseManager::s_staticMemberLock;
  21. // Static member management
  22. BGParseManager* BGParseManager::GetBGParseManager()
  23. {
  24. AutoCriticalSection lock(&s_staticMemberLock);
  25. if (s_BGParseManager == nullptr)
  26. {
  27. AUTO_NESTED_HANDLED_EXCEPTION_TYPE(ExceptionType_DisableCheck);
  28. s_BGParseManager = HeapNew(BGParseManager);
  29. s_BGParseManager->Processor()->AddManager(s_BGParseManager);
  30. }
  31. return s_BGParseManager;
  32. }
  33. void BGParseManager::DeleteBGParseManager()
  34. {
  35. AutoCriticalSection lock(&s_staticMemberLock);
  36. if (s_BGParseManager != nullptr)
  37. {
  38. BGParseManager* manager = s_BGParseManager;
  39. s_BGParseManager = nullptr;
  40. HeapDelete(manager);
  41. }
  42. }
  43. DWORD BGParseManager::GetNextCookie()
  44. {
  45. AutoCriticalSection lock(&s_staticMemberLock);
  46. return ++s_lastCookie;
  47. }
  48. DWORD BGParseManager::IncCompleted()
  49. {
  50. AutoCriticalSection lock(&s_staticMemberLock);
  51. return ++s_completed;
  52. }
  53. DWORD BGParseManager::IncFailed()
  54. {
  55. AutoCriticalSection lock(&s_staticMemberLock);
  56. return ++s_failed;
  57. }
  58. // Note: runs on any thread
  59. BGParseManager::BGParseManager()
  60. : JsUtil::WaitableJobManager(ThreadBoundThreadContextManager::GetSharedJobProcessor())
  61. {
  62. }
  63. // Note: runs on any thread
  64. BGParseManager::~BGParseManager()
  65. {
  66. // First, remove the manager from the JobProcessor so that any remaining jobs
  67. // are moved back into this manager's processed workitem list
  68. Processor()->RemoveManager(this);
  69. Assert(this->workitemsProcessing.IsEmpty());
  70. // Now, free all remaining processed jobs
  71. BGParseWorkItem *item = (BGParseWorkItem*)this->workitemsProcessed.Head();
  72. while (item != nullptr)
  73. {
  74. // Get the next item first so that the current item
  75. // can be safely removed and freed
  76. BGParseWorkItem* temp = item;
  77. item = (BGParseWorkItem*)item->Next();
  78. this->workitemsProcessed.Unlink(temp);
  79. HeapDelete(temp);
  80. }
  81. }
  82. // Returns the BGParseWorkItem that matches the provided cookie. Parameters have the following impact:
  83. // - waitForResults: creates an event on the returned WorkItem that the caller can wait for
  84. // - removeJob: removes the job from the list that contained it, and marks it for discard if it is processing
  85. // Note: runs on any thread
  86. BGParseWorkItem* BGParseManager::FindJob(DWORD dwCookie, bool waitForResults, bool removeJob)
  87. {
  88. Assert(dwCookie != 0);
  89. Assert(!waitForResults || !removeJob);
  90. AutoOptionalCriticalSection autoLock(Processor()->GetCriticalSection());
  91. BGParseWorkItem* matchedWorkitem = nullptr;
  92. // First, look among processed jobs
  93. for (BGParseWorkItem *item = this->workitemsProcessed.Head(); item != nullptr && matchedWorkitem == nullptr; item = (BGParseWorkItem*)item->Next())
  94. {
  95. if (item->GetCookie() == dwCookie)
  96. {
  97. matchedWorkitem = item;
  98. if (removeJob)
  99. {
  100. this->workitemsProcessed.Unlink(matchedWorkitem);
  101. }
  102. }
  103. }
  104. if (matchedWorkitem == nullptr)
  105. {
  106. // Then, look among processing jobs
  107. for (BGParseWorkItem *item = this->workitemsProcessing.Head(); item != nullptr && matchedWorkitem == nullptr; item = (BGParseWorkItem*)item->Next())
  108. {
  109. if (item->GetCookie() == dwCookie)
  110. {
  111. matchedWorkitem = item;
  112. if (removeJob)
  113. {
  114. this->workitemsProcessing.Unlink(matchedWorkitem);
  115. // Since the job is still processing, it cannot be freed immediately. Mark it as discarded so
  116. // that it can be freed later.
  117. matchedWorkitem->Discard();
  118. }
  119. }
  120. }
  121. // Lastly, look among queued jobs
  122. if (matchedWorkitem == nullptr)
  123. {
  124. Processor()->ForEachJob([&](JsUtil::Job * job) {
  125. if (job->Manager() == this)
  126. {
  127. BGParseWorkItem* workitem = (BGParseWorkItem*)job;
  128. if (workitem->GetCookie() == dwCookie)
  129. {
  130. matchedWorkitem = workitem;
  131. return false;
  132. }
  133. }
  134. return true;
  135. });
  136. if (removeJob && matchedWorkitem != nullptr)
  137. {
  138. Processor()->RemoveJob(matchedWorkitem);
  139. }
  140. }
  141. // Since this job isn't already processed and the caller needs the results, create an event
  142. // that the caller can wait on for results to complete
  143. if (waitForResults && matchedWorkitem != nullptr)
  144. {
  145. // TODO: Is it possible for one event to be shared to reduce the number of heap allocations?
  146. matchedWorkitem->CreateCompletionEvent();
  147. }
  148. }
  149. return matchedWorkitem;
  150. }
  151. // Creates a new job to parse the provided script on a background thread
  152. // Note: runs on any thread
  153. HRESULT BGParseManager::QueueBackgroundParse(LPCUTF8 pszSrc, size_t cbLength, char16 *fullPath, DWORD* dwBgParseCookie)
  154. {
  155. HRESULT hr = S_OK;
  156. if (cbLength > 0)
  157. {
  158. BGParseWorkItem* workitem;
  159. {
  160. AUTO_NESTED_HANDLED_EXCEPTION_TYPE(ExceptionType_DisableCheck);
  161. workitem = HeapNew(BGParseWorkItem, this, (const byte *)pszSrc, cbLength, fullPath);
  162. }
  163. // Add the job to the processor
  164. {
  165. AutoOptionalCriticalSection autoLock(Processor()->GetCriticalSection());
  166. Processor()->AddJob(workitem, false /*prioritize*/);
  167. }
  168. (*dwBgParseCookie) = workitem->GetCookie();
  169. if (PHASE_TRACE1(Js::BgParsePhase))
  170. {
  171. Js::Tick now = Js::Tick::Now();
  172. Output::Print(
  173. _u("[BgParse: Start -- cookie: %04d on thread 0x%X at %.2f ms -- %s]\n"),
  174. workitem->GetCookie(),
  175. ::GetCurrentThreadId(),
  176. now.ToMilliseconds(),
  177. fullPath
  178. );
  179. }
  180. }
  181. else
  182. {
  183. hr = E_INVALIDARG;
  184. }
  185. return hr;
  186. }
  187. // Returns the data provided when the parse was queued
  188. // Note: runs on any thread, but the buffer lifetimes are not guaranteed after parse results are returned
  189. HRESULT BGParseManager::GetInputFromCookie(DWORD cookie, LPCUTF8* ppszSrc, size_t* pcbLength, WCHAR** sourceUrl)
  190. {
  191. HRESULT hr = E_FAIL;
  192. // Find the job associated with this cookie
  193. BGParseWorkItem* workitem = FindJob(cookie, false /*waitForResults*/, false /*removeJob*/);
  194. if (workitem != nullptr)
  195. {
  196. if (ppszSrc != nullptr)
  197. {
  198. (*ppszSrc) = workitem->GetScriptSrc();
  199. }
  200. if (pcbLength != nullptr)
  201. {
  202. (*pcbLength) = workitem->GetScriptLength();
  203. }
  204. if (sourceUrl != nullptr)
  205. {
  206. (*sourceUrl) = workitem->GetScriptPath();
  207. }
  208. hr = S_OK;
  209. }
  210. return hr;
  211. }
  212. // Deserializes the background parse results into this thread
  213. // Note: *must* run on a UI/Execution thread with an available ScriptContext
  214. HRESULT BGParseManager::GetParseResults(
  215. Js::ScriptContext* scriptContextUI,
  216. DWORD cookie,
  217. LPCUTF8 pszSrc,
  218. SRCINFO const * pSrcInfo,
  219. Js::FunctionBody** ppFunc,
  220. CompileScriptException* pse,
  221. size_t& srcLength,
  222. Js::Utf8SourceInfo* utf8SourceInfo,
  223. uint& sourceIndex)
  224. {
  225. // TODO: Is there a way to cache the environment from which serialization begins to
  226. // determine whether or not deserialization will succeed? Specifically, being able
  227. // to assert/compare the flags used during background parse with the flags expected
  228. // from the UI thread?
  229. HRESULT hr = E_FAIL;
  230. // Find the job associated with this cookie
  231. BGParseWorkItem* workitem = FindJob(cookie, true /*waitForResults*/, false /*removeJob*/);
  232. if (workitem != nullptr)
  233. {
  234. // Synchronously wait for the job to complete
  235. workitem->WaitForCompletion();
  236. Js::FunctionBody* functionBody = nullptr;
  237. hr = workitem->DeserializeParseResults(scriptContextUI, pszSrc, pSrcInfo, utf8SourceInfo, &functionBody, srcLength, sourceIndex);
  238. (*ppFunc) = functionBody;
  239. if (pse != nullptr)
  240. {
  241. workitem->TransferCSE(pse);
  242. }
  243. if (hr == S_OK)
  244. {
  245. BGParseManager::IncCompleted();
  246. }
  247. else
  248. {
  249. BGParseManager::IncFailed();
  250. }
  251. }
  252. if (PHASE_TRACE1(Js::BgParsePhase))
  253. {
  254. Js::Tick now = Js::Tick::Now();
  255. Output::Print(
  256. _u("[BgParse: End -- cookie: %04d on thread 0x%X at %.2f ms -- hr: 0x%X]\n"),
  257. workitem != nullptr ? workitem->GetCookie() : -1,
  258. ::GetCurrentThreadId(),
  259. now.ToMilliseconds(),
  260. hr
  261. );
  262. }
  263. return hr;
  264. }
  265. // Finds and removes the workitem associated with the provided cookie. If the workitem is processed
  266. // or not yet processed, the workitem is simply removed and freed. If the workitem is being processed,
  267. // it is removed from the list and will be freed after the job is processed (with its script source
  268. // buffer).
  269. // Returns true when the caller should free the script source buffer. Otherwise, when false is returned,
  270. // the workitem is responsible for freeing the script source buffer.
  271. bool BGParseManager::DiscardParseResults(DWORD cookie, void* buffer)
  272. {
  273. BGParseWorkItem* workitem = FindJob(cookie, false /*waitForResults*/, true /*removeJob*/);
  274. bool callerOwnsSourceBuffer = true;
  275. if (workitem != nullptr)
  276. {
  277. Assert(buffer == workitem->GetScriptSrc());
  278. if (!workitem->IsDiscarded())
  279. {
  280. HeapDelete(workitem);
  281. }
  282. else
  283. {
  284. callerOwnsSourceBuffer = false;
  285. }
  286. }
  287. if (PHASE_TRACE1(Js::BgParsePhase))
  288. {
  289. Js::Tick now = Js::Tick::Now();
  290. Output::Print(
  291. _u("[BgParse: Discard -- cookie: %04d on thread 0x%X at %.2f ms, workitem: 0x%p, workitem owns buffer: %u]\n"),
  292. cookie,
  293. ::GetCurrentThreadId(),
  294. now.ToMilliseconds(),
  295. workitem,
  296. !callerOwnsSourceBuffer
  297. );
  298. }
  299. return callerOwnsSourceBuffer;
  300. }
  301. // Overloaded function called by JobProcessor to do work
  302. // Note: runs on background thread
  303. bool BGParseManager::Process(JsUtil::Job *const job, JsUtil::ParallelThreadData *threadData)
  304. {
  305. #if ENABLE_BACKGROUND_JOB_PROCESSOR
  306. Assert(job->Manager() == this);
  307. // Create script context on this thread
  308. ThreadContext* threadContext = ThreadBoundThreadContextManager::EnsureContextForCurrentThread();
  309. // If there is no script context created for this thread yet, create it now
  310. if (threadData->scriptContextBG == nullptr)
  311. {
  312. threadData->scriptContextBG = Js::ScriptContext::New(threadContext);
  313. threadData->scriptContextBG->Initialize();
  314. threadData->canDecommit = true;
  315. }
  316. // Parse the workitem's data
  317. BGParseWorkItem* workItem = (BGParseWorkItem*)job;
  318. workItem->ParseUTF8Core(threadData->scriptContextBG);
  319. return true;
  320. #else
  321. Assert(!"BGParseManager does not work without ThreadContext");
  322. return false;
  323. #endif
  324. }
  325. // Callback before the provided job will be processed
  326. // Note: runs on any thread
  327. void BGParseManager::JobProcessing(JsUtil::Job* job)
  328. {
  329. Assert(job->Manager() == this);
  330. Assert(Processor()->GetCriticalSection()->IsLocked());
  331. this->workitemsProcessing.LinkToEnd((BGParseWorkItem*)job);
  332. }
  333. // Callback after the provided job was processed. succeeded is true if the job
  334. // was executed as well.
  335. // Note: runs on any thread
  336. void BGParseManager::JobProcessed(JsUtil::Job *const job, const bool succeeded)
  337. {
  338. Assert(job->Manager() == this);
  339. Assert(Processor()->GetCriticalSection()->IsLocked());
  340. BGParseWorkItem* workItem = (BGParseWorkItem*)job;
  341. if (succeeded)
  342. {
  343. Assert(!this->workitemsProcessed.Contains(workItem));
  344. if (this->workitemsProcessing.Contains(workItem))
  345. {
  346. // Move this processed workitem from the processing list to
  347. // the processed list
  348. this->workitemsProcessing.Unlink(workItem);
  349. this->workitemsProcessed.LinkToEnd(workItem);
  350. }
  351. else
  352. {
  353. // If this workitem isn't in the processing queue, it should
  354. // already be discarded
  355. Assert(workItem->IsDiscarded());
  356. }
  357. }
  358. else
  359. {
  360. // When the manager is shutting down, workitems are processed through the JobProcessor
  361. // without executing (i.e., !succeeded). So, mark it for discard so that it can be freed.
  362. workItem->Discard();
  363. }
  364. workItem->JobProcessed(succeeded);
  365. }
  366. // Define needed for jobs.inl
  367. // Note: Runs on any thread
  368. BGParseWorkItem* BGParseManager::GetJob(BGParseWorkItem* workitem)
  369. {
  370. Assert(!"BGParseManager::WasAddedToJobProcessor");
  371. return nullptr;
  372. }
  373. // Define needed for jobs.inl
  374. // Note: Runs on any thread
  375. bool BGParseManager::WasAddedToJobProcessor(JsUtil::Job *const job) const
  376. {
  377. Assert(!"BGParseManager::WasAddedToJobProcessor");
  378. return true;
  379. }
  380. // Note: runs on any thread
  381. BGParseWorkItem::BGParseWorkItem(
  382. BGParseManager* manager,
  383. const byte* pszScript,
  384. size_t cbScript,
  385. char16 *fullPath
  386. )
  387. : JsUtil::Job(manager),
  388. script(pszScript),
  389. cb(cbScript),
  390. path(nullptr),
  391. parseHR(S_OK),
  392. parseSourceLength(0),
  393. bufferReturn(nullptr),
  394. bufferReturnBytes(0),
  395. complete(nullptr),
  396. discarded(false)
  397. {
  398. this->cookie = BGParseManager::GetNextCookie();
  399. Assert(fullPath != nullptr);
  400. this->path = SysAllocString(fullPath);
  401. }
  402. BGParseWorkItem::~BGParseWorkItem()
  403. {
  404. SysFreeString(this->path);
  405. if (this->complete != nullptr)
  406. {
  407. HeapDelete(this->complete);
  408. }
  409. if (this->bufferReturn != nullptr)
  410. {
  411. ::CoTaskMemFree(this->bufferReturn);
  412. }
  413. if (this->discarded)
  414. {
  415. // When this workitem has been discarded, this is the last reference
  416. // to the script source, so free it now during destruction.
  417. ::HeapFree(GetProcessHeap(), 0, (void*)this->script);
  418. }
  419. }
  420. void BGParseWorkItem::TransferCSE(CompileScriptException* pse)
  421. {
  422. this->cse.CopyInto(pse);
  423. }
  424. // This function parses the input data cached in BGParseWorkItem and caches the
  425. // seralized bytecode
  426. // Note: runs on BackgroundJobProcessor thread
  427. // Note: All exceptions are caught by BackgroundJobProcessor
  428. void BGParseWorkItem::ParseUTF8Core(Js::ScriptContext* scriptContext)
  429. {
  430. if (PHASE_TRACE1(Js::BgParsePhase))
  431. {
  432. Js::Tick now = Js::Tick::Now();
  433. Output::Print(
  434. _u("[BgParse: Parse -- cookie: %04d on thread 0x%X at %.2f ms]\n"),
  435. GetCookie(),
  436. ::GetCurrentThreadId(),
  437. now.ToMilliseconds()
  438. );
  439. }
  440. Js::AutoDynamicCodeReference dynamicFunctionReference(scriptContext);
  441. SourceContextInfo* sourceContextInfo = scriptContext->GetSourceContextInfo(this->cookie, nullptr);
  442. if (sourceContextInfo == nullptr)
  443. {
  444. sourceContextInfo = scriptContext->CreateSourceContextInfo(this->cookie, this->path, wcslen(this->path), nullptr);
  445. }
  446. SRCINFO si = {
  447. sourceContextInfo,
  448. 0, // dlnHost
  449. 0, // ulColumnHost
  450. 0, // lnMinHost
  451. 0, // ichMinHost
  452. static_cast<ULONG>(cb / sizeof(utf8char_t)), // ichLimHost
  453. 0, // ulCharOffset
  454. kmodGlobal, // mod
  455. 0 // grfsi
  456. };
  457. ENTER_PINNED_SCOPE(Js::Utf8SourceInfo, sourceInfo);
  458. sourceInfo = Js::Utf8SourceInfo::NewWithNoCopy(scriptContext, (LPUTF8)this->script, (int32)this->cb, static_cast<int32>(this->cb), &si, false);
  459. charcount_t cchLength = 0;
  460. uint sourceIndex = 0;
  461. Js::ParseableFunctionInfo * func = nullptr;
  462. Parser ps(scriptContext);
  463. this->parseHR = scriptContext->CompileUTF8Core(
  464. ps,
  465. sourceInfo,
  466. &si,
  467. true, // fOriginalUtf8Code
  468. this->script,
  469. this->cb,
  470. BGPARSE_FLAGS,
  471. &this->cse,
  472. cchLength,
  473. this->parseSourceLength,
  474. sourceIndex,
  475. &func,
  476. nullptr // pDataCache
  477. );
  478. if (this->parseHR == S_OK)
  479. {
  480. BEGIN_TEMP_ALLOCATOR(tempAllocator, scriptContext, _u("BGParseWorkItem"));
  481. Js::FunctionBody *functionBody = func->GetFunctionBody();
  482. this->parseHR = Js::ByteCodeSerializer::SerializeToBuffer(
  483. scriptContext,
  484. tempAllocator,
  485. (DWORD)this->cb,
  486. this->script,
  487. functionBody,
  488. functionBody->GetHostSrcInfo(),
  489. &this->bufferReturn,
  490. &this->bufferReturnBytes,
  491. GENERATE_BYTE_CODE_PARSER_STATE | GENERATE_BYTE_CODE_COTASKMEMALLOC
  492. );
  493. END_TEMP_ALLOCATOR(tempAllocator, scriptContext);
  494. Assert(this->parseHR == S_OK);
  495. }
  496. else
  497. {
  498. Assert(this->cse.ei.bstrSource != nullptr);
  499. Assert(func == nullptr);
  500. }
  501. LEAVE_PINNED_SCOPE();
  502. }
  503. // Deserializes the background parse results into this thread
  504. // Note: *must* run on a UI/Execution thread with an available ScriptContext
  505. HRESULT BGParseWorkItem::DeserializeParseResults(
  506. Js::ScriptContext* scriptContextUI,
  507. LPCUTF8 pszSrc,
  508. SRCINFO const * pSrcInfo,
  509. Js::Utf8SourceInfo* utf8SourceInfo,
  510. Js::FunctionBody** functionBodyReturn,
  511. size_t& srcLength,
  512. uint& sourceIndex
  513. )
  514. {
  515. HRESULT hr = this->parseHR;
  516. if (hr == S_OK)
  517. {
  518. if (utf8SourceInfo == nullptr)
  519. {
  520. scriptContextUI->MakeUtf8SourceInfo(
  521. this->script,
  522. this->cb,
  523. pSrcInfo,
  524. &utf8SourceInfo,
  525. LoadScriptFlag_Utf8Source,
  526. nullptr // Var scriptSource
  527. );
  528. }
  529. srcLength = this->parseSourceLength;
  530. sourceIndex = scriptContextUI->SaveSourceNoCopy(utf8SourceInfo, (int)srcLength, false /*isCesu8*/);
  531. Assert(sourceIndex != Js::Constants::InvalidSourceIndex);
  532. Field(Js::FunctionBody*) functionBody = nullptr;
  533. hr = Js::ByteCodeSerializer::DeserializeFromBuffer(
  534. scriptContextUI,
  535. BGPARSE_FLAGS,
  536. (const byte *)pszSrc,
  537. pSrcInfo,
  538. this->bufferReturn,
  539. nullptr, // nativeModule
  540. &functionBody,
  541. sourceIndex
  542. );
  543. if (hr == S_OK)
  544. {
  545. // The buffer is now owned by the output of DeserializeFromBuffer
  546. (*functionBodyReturn) = functionBody;
  547. this->bufferReturn = nullptr;
  548. this->bufferReturnBytes = 0;
  549. }
  550. }
  551. return hr;
  552. }
  553. void BGParseWorkItem::CreateCompletionEvent()
  554. {
  555. Assert(this->complete == nullptr);
  556. this->complete = HeapNew(Event, false);
  557. }
  558. // Upon notification of job processed, set the event for those waiting for this job to complete
  559. void BGParseWorkItem::JobProcessed(const bool succeeded)
  560. {
  561. Assert(Manager()->Processor()->GetCriticalSection()->IsLocked());
  562. if (IsDiscarded())
  563. {
  564. Js::Tick now = Js::Tick::Now();
  565. Output::Print(
  566. _u("[BgParse: Discard Before GetResults -- cookie: %04d on thread 0x%X at %.2f ms]\n"),
  567. GetCookie(),
  568. ::GetCurrentThreadId(),
  569. now.ToMilliseconds()
  570. );
  571. // When a workitem has been discarded while processing, there are now other
  572. // references to it, so free it now
  573. Assert((this->Next() == nullptr && this->Previous() == nullptr) || !succeeded);
  574. HeapDelete(this);
  575. }
  576. else if (this->complete != nullptr)
  577. {
  578. this->complete->Set();
  579. }
  580. }
  581. // Wait for this job to finish processing
  582. void BGParseWorkItem::WaitForCompletion()
  583. {
  584. if (this->complete != nullptr)
  585. {
  586. if (PHASE_TRACE1(Js::BgParsePhase))
  587. {
  588. Js::Tick now = Js::Tick::Now();
  589. Output::Print(
  590. _u("[BgParse: Wait -- cookie: %04d on thread 0x%X at %.2f ms]\n"),
  591. GetCookie(),
  592. ::GetCurrentThreadId(),
  593. now.ToMilliseconds()
  594. );
  595. }
  596. this->complete->Wait();
  597. }
  598. }