BackgroundParser.cpp 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  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. #define ASSERT_THREAD() AssertMsg(mainThreadId == GetCurrentThreadContextId(), \
  7. "Cannot use this member of BackgroundParser from thread other than the creating context's current thread")
  8. #if ENABLE_NATIVE_CODEGEN
  9. #if ENABLE_BACKGROUND_PARSING
  10. BackgroundParser::BackgroundParser(Js::ScriptContext *scriptContext)
  11. : JsUtil::WaitableJobManager(scriptContext->GetThreadContext()->GetJobProcessor()),
  12. scriptContext(scriptContext),
  13. unprocessedItemsHead(nullptr),
  14. unprocessedItemsTail(nullptr),
  15. failedBackgroundParseItem(nullptr),
  16. pendingBackgroundItems(0)
  17. {
  18. Processor()->AddManager(this);
  19. #if DBG
  20. this->mainThreadId = GetCurrentThreadContextId();
  21. #endif
  22. }
  23. BackgroundParser::~BackgroundParser()
  24. {
  25. JsUtil::JobProcessor *processor = Processor();
  26. if (processor->ProcessesInBackground())
  27. {
  28. static_cast<JsUtil::BackgroundJobProcessor*>(processor)->IterateBackgroundThreads([&](JsUtil::ParallelThreadData *threadData)->bool {
  29. if (threadData->parser)
  30. {
  31. // Adelete to make sure dtor are called (since the HashTbl has its NoReleaseAllocator)
  32. Adelete(threadData->threadArena, threadData->parser);
  33. threadData->parser = nullptr;
  34. }
  35. return false;
  36. });
  37. }
  38. processor->RemoveManager(this);
  39. }
  40. BackgroundParser * BackgroundParser::New(Js::ScriptContext *scriptContext)
  41. {
  42. return HeapNew(BackgroundParser, scriptContext);
  43. }
  44. void BackgroundParser::Delete(BackgroundParser *backgroundParser)
  45. {
  46. HeapDelete(backgroundParser);
  47. }
  48. bool BackgroundParser::Process(JsUtil::Job *const job, JsUtil::ParallelThreadData *threadData)
  49. {
  50. BackgroundParseItem *backgroundItem = static_cast<BackgroundParseItem*>(job);
  51. if (failedBackgroundParseItem)
  52. {
  53. if (backgroundItem->GetParseNode()->ichMin > failedBackgroundParseItem->GetParseNode()->ichMin)
  54. {
  55. return true;
  56. }
  57. }
  58. if (threadData->parser == nullptr || threadData->canDecommit)
  59. {
  60. if (threadData->parser != nullptr)
  61. {
  62. // "canDecommit" means the previous parse finished.
  63. // Don't leave a parser with stale state in the thread data, or we'll mess up the bindings.
  64. threadData->backgroundPageAllocator.DecommitNow();
  65. this->OnDecommit(threadData);
  66. }
  67. threadData->canDecommit = false;
  68. // Lazily create a parser instance for this thread from the thread's page allocator.
  69. // It will stay around until the main thread's current parser instance goes away, which will free
  70. // the background thread to decommit its pages.
  71. threadData->parser = Anew(threadData->threadArena, Parser, this->scriptContext, backgroundItem->IsStrictMode(), &threadData->backgroundPageAllocator, true);
  72. threadData->pse = Anew(threadData->threadArena, CompileScriptException);
  73. }
  74. Parser *parser = threadData->parser;
  75. return this->Process(backgroundItem, parser, threadData->pse);
  76. }
  77. bool BackgroundParser::Process(JsUtil::Job *const job, Parser *parser, CompileScriptException *pse)
  78. {
  79. BackgroundParseItem *backgroundItem = static_cast<BackgroundParseItem*>(job);
  80. Assert(parser->GetCurrBackgroundParseItem() == nullptr);
  81. parser->SetCurrBackgroundParseItem(backgroundItem);
  82. backgroundItem->SetParser(parser);
  83. HRESULT hr = parser->ParseFunctionInBackground(backgroundItem->GetParseNode(), backgroundItem->GetParseContext(), backgroundItem->IsDeferred(), pse);
  84. backgroundItem->SetMaxBlockId(parser->GetLastBlockId());
  85. backgroundItem->SetHR(hr);
  86. if (FAILED(hr))
  87. {
  88. backgroundItem->SetPSE(pse);
  89. }
  90. backgroundItem->SetCompleted(true);
  91. parser->SetCurrBackgroundParseItem(nullptr);
  92. return hr == S_OK;
  93. }
  94. void BackgroundParser::JobProcessed(JsUtil::Job *const job, const bool succeeded)
  95. {
  96. // This is called from inside a lock, so we can mess with background parser attributes.
  97. BackgroundParseItem *backgroundItem = static_cast<BackgroundParseItem*>(job);
  98. this->RemoveFromUnprocessedItems(backgroundItem);
  99. --this->pendingBackgroundItems;
  100. if (!succeeded)
  101. {
  102. Assert(FAILED(backgroundItem->GetHR()) || failedBackgroundParseItem);
  103. if (FAILED(backgroundItem->GetHR()))
  104. {
  105. if (!failedBackgroundParseItem)
  106. {
  107. failedBackgroundParseItem = backgroundItem;
  108. }
  109. else
  110. {
  111. // If syntax errors are detected on multiple threads, the lexically earlier one should win.
  112. CompileScriptException *newPse = backgroundItem->GetPSE();
  113. CompileScriptException *oldPse = failedBackgroundParseItem->GetPSE();
  114. if (newPse->line < oldPse->line ||
  115. (newPse->line == oldPse->line && newPse->ichMinLine < oldPse->ichMinLine))
  116. {
  117. failedBackgroundParseItem = backgroundItem;
  118. }
  119. }
  120. }
  121. }
  122. }
  123. void BackgroundParser::OnDecommit(JsUtil::ParallelThreadData *threadData)
  124. {
  125. if (threadData->parser)
  126. {
  127. // Adelete to make sure dtor are called (since the HashTbl has its NoReleaseAllocator)
  128. Adelete(threadData->threadArena, threadData->parser);
  129. threadData->parser = nullptr;
  130. }
  131. }
  132. BackgroundParseItem * BackgroundParser::NewBackgroundParseItem(Parser *parser, ParseNodeFnc *parseNode, bool isDeferred)
  133. {
  134. BackgroundParseItem *item = Anew(parser->GetAllocator(), BackgroundParseItem, this, parser, parseNode, isDeferred);
  135. parser->AddBackgroundParseItem(item);
  136. return item;
  137. }
  138. bool BackgroundParser::ParseBackgroundItem(Parser *parser, ParseNodeFnc *parseNode, bool isDeferred)
  139. {
  140. ASSERT_THREAD();
  141. AutoPtr<BackgroundParseItem> workItemAutoPtr(this->NewBackgroundParseItem(parser, parseNode, isDeferred));
  142. if ((BackgroundParseItem*) workItemAutoPtr == nullptr)
  143. {
  144. // OOM, just skip this work item and return.
  145. // TODO: Raise an OOM parse-time exception.
  146. return false;
  147. }
  148. parser->PrepareForBackgroundParse();
  149. BackgroundParseItem * backgroundItem = workItemAutoPtr.Detach();
  150. this->AddToParseQueue(backgroundItem, false, this->Processor()->ProcessesInBackground());
  151. return true;
  152. }
  153. BackgroundParseItem *BackgroundParser::GetJob(BackgroundParseItem *workitem) const
  154. {
  155. return workitem;
  156. }
  157. bool BackgroundParser::WasAddedToJobProcessor(JsUtil::Job *const job) const
  158. {
  159. ASSERT_THREAD();
  160. Assert(job);
  161. return static_cast<BackgroundParseItem*>(job)->IsInParseQueue();
  162. }
  163. void BackgroundParser::BeforeWaitForJob(BackgroundParseItem *const item) const
  164. {
  165. }
  166. void BackgroundParser::AfterWaitForJob(BackgroundParseItem *const item) const
  167. {
  168. }
  169. void BackgroundParser::AddToParseQueue(BackgroundParseItem *const item, bool prioritize, bool lock)
  170. {
  171. AutoOptionalCriticalSection autoLock(lock ? Processor()->GetCriticalSection() : nullptr);
  172. ++this->pendingBackgroundItems;
  173. Processor()->AddJob(item, prioritize); // This one can throw (really unlikely though), OOM specifically.
  174. this->AddUnprocessedItem(item);
  175. item->OnAddToParseQueue();
  176. }
  177. void BackgroundParser::AddUnprocessedItem(BackgroundParseItem *const item)
  178. {
  179. if (this->unprocessedItemsTail == nullptr)
  180. {
  181. this->unprocessedItemsHead = item;
  182. }
  183. else
  184. {
  185. this->unprocessedItemsTail->SetNextUnprocessedItem(item);
  186. }
  187. item->SetPrevUnprocessedItem(this->unprocessedItemsTail);
  188. this->unprocessedItemsTail = item;
  189. }
  190. void BackgroundParser::RemoveFromUnprocessedItems(BackgroundParseItem *const item)
  191. {
  192. if (this->unprocessedItemsHead == item)
  193. {
  194. this->unprocessedItemsHead = item->GetNextUnprocessedItem();
  195. }
  196. else
  197. {
  198. item->GetPrevUnprocessedItem()->SetNextUnprocessedItem(item->GetNextUnprocessedItem());
  199. }
  200. if (this->unprocessedItemsTail == item)
  201. {
  202. this->unprocessedItemsTail = item->GetPrevUnprocessedItem();
  203. }
  204. else
  205. {
  206. item->GetNextUnprocessedItem()->SetPrevUnprocessedItem(item->GetPrevUnprocessedItem());
  207. }
  208. item->SetNextUnprocessedItem(nullptr);
  209. item->SetPrevUnprocessedItem(nullptr);
  210. }
  211. BackgroundParseItem *BackgroundParser::GetNextUnprocessedItem() const
  212. {
  213. BackgroundParseItem *item;
  214. bool background = this->Processor()->ProcessesInBackground();
  215. for (item = this->unprocessedItemsHead; item; item = item->GetNextUnprocessedItem())
  216. {
  217. if (!background || !static_cast<JsUtil::BackgroundJobProcessor*>(Processor())->IsBeingProcessed(item))
  218. {
  219. return item;
  220. }
  221. }
  222. return nullptr;
  223. }
  224. BackgroundParseItem::BackgroundParseItem(JsUtil::JobManager *const manager, Parser *const parser, ParseNodeFnc *parseNode, bool defer)
  225. : JsUtil::Job(manager),
  226. maxBlockId((uint)-1),
  227. strictMode(parser->IsStrictMode()),
  228. parseNode(parseNode),
  229. parser(nullptr),
  230. nextItem(nullptr),
  231. nextUnprocessedItem(nullptr),
  232. prevUnprocessedItem(nullptr),
  233. pse(nullptr),
  234. regExpNodes(nullptr),
  235. completed(false),
  236. inParseQueue(false),
  237. isDeferred(defer)
  238. {
  239. parser->CaptureContext(&parseContext);
  240. }
  241. void BackgroundParseItem::OnAddToParseQueue()
  242. {
  243. this->inParseQueue = true;
  244. }
  245. void BackgroundParseItem::OnRemoveFromParseQueue()
  246. {
  247. this->inParseQueue = false;
  248. }
  249. void BackgroundParseItem::AddRegExpNode(ParseNode *const pnode, ArenaAllocator *alloc)
  250. {
  251. if (regExpNodes == nullptr)
  252. {
  253. regExpNodes = Anew(alloc, NodeDList, alloc);
  254. }
  255. regExpNodes->Append(pnode);
  256. }
  257. #endif
  258. #endif