BackgroundParser.cpp 9.6 KB

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