| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297 |
- //-------------------------------------------------------------------------------------------------------
- // Copyright (C) Microsoft. All rights reserved.
- // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
- //-------------------------------------------------------------------------------------------------------
- #include "ParserPch.h"
- #define ASSERT_THREAD() AssertMsg(mainThreadId == GetCurrentThreadContextId(), \
- "Cannot use this member of BackgroundParser from thread other than the creating context's current thread")
- #if ENABLE_NATIVE_CODEGEN
- #if ENABLE_BACKGROUND_PARSING
- BackgroundParser::BackgroundParser(Js::ScriptContext *scriptContext)
- : JsUtil::WaitableJobManager(scriptContext->GetThreadContext()->GetJobProcessor()),
- scriptContext(scriptContext),
- unprocessedItemsHead(nullptr),
- unprocessedItemsTail(nullptr),
- failedBackgroundParseItem(nullptr),
- pendingBackgroundItems(0)
- {
- Processor()->AddManager(this);
- #if DBG
- this->mainThreadId = GetCurrentThreadContextId();
- #endif
- }
- BackgroundParser::~BackgroundParser()
- {
- JsUtil::JobProcessor *processor = Processor();
- if (processor->ProcessesInBackground())
- {
- static_cast<JsUtil::BackgroundJobProcessor*>(processor)->IterateBackgroundThreads([&](JsUtil::ParallelThreadData *threadData)->bool {
- if (threadData->parser)
- {
- // Adelete to make sure dtor are called (since the HashTbl has its NoReleaseAllocator)
- Adelete(threadData->threadArena, threadData->parser);
- threadData->parser = nullptr;
- }
- return false;
- });
- }
- processor->RemoveManager(this);
- }
- BackgroundParser * BackgroundParser::New(Js::ScriptContext *scriptContext)
- {
- return HeapNew(BackgroundParser, scriptContext);
- }
- void BackgroundParser::Delete(BackgroundParser *backgroundParser)
- {
- HeapDelete(backgroundParser);
- }
- bool BackgroundParser::Process(JsUtil::Job *const job, JsUtil::ParallelThreadData *threadData)
- {
- BackgroundParseItem *backgroundItem = static_cast<BackgroundParseItem*>(job);
- if (failedBackgroundParseItem)
- {
- if (backgroundItem->GetParseNode()->ichMin > failedBackgroundParseItem->GetParseNode()->ichMin)
- {
- return true;
- }
- }
- if (threadData->parser == nullptr || threadData->canDecommit)
- {
- if (threadData->parser != nullptr)
- {
- // "canDecommit" means the previous parse finished.
- // Don't leave a parser with stale state in the thread data, or we'll mess up the bindings.
- threadData->backgroundPageAllocator.DecommitNow();
- this->OnDecommit(threadData);
- }
- threadData->canDecommit = false;
- // Lazily create a parser instance for this thread from the thread's page allocator.
- // It will stay around until the main thread's current parser instance goes away, which will free
- // the background thread to decommit its pages.
- threadData->parser = Anew(threadData->threadArena, Parser, this->scriptContext, backgroundItem->IsStrictMode(), &threadData->backgroundPageAllocator, true);
- threadData->pse = Anew(threadData->threadArena, CompileScriptException);
- }
- Parser *parser = threadData->parser;
- return this->Process(backgroundItem, parser, threadData->pse);
- }
- bool BackgroundParser::Process(JsUtil::Job *const job, Parser *parser, CompileScriptException *pse)
- {
- BackgroundParseItem *backgroundItem = static_cast<BackgroundParseItem*>(job);
- Assert(parser->GetCurrBackgroundParseItem() == nullptr);
- parser->SetCurrBackgroundParseItem(backgroundItem);
- backgroundItem->SetParser(parser);
- HRESULT hr = parser->ParseFunctionInBackground(backgroundItem->GetParseNode(), backgroundItem->GetParseContext(), backgroundItem->IsDeferred(), pse);
- backgroundItem->SetMaxBlockId(parser->GetLastBlockId());
- backgroundItem->SetHR(hr);
- if (FAILED(hr))
- {
- backgroundItem->SetPSE(pse);
- }
- backgroundItem->SetCompleted(true);
- parser->SetCurrBackgroundParseItem(nullptr);
- return hr == S_OK;
- }
- void BackgroundParser::JobProcessed(JsUtil::Job *const job, const bool succeeded)
- {
- // This is called from inside a lock, so we can mess with background parser attributes.
- BackgroundParseItem *backgroundItem = static_cast<BackgroundParseItem*>(job);
- this->RemoveFromUnprocessedItems(backgroundItem);
- --this->pendingBackgroundItems;
- if (!succeeded)
- {
- Assert(FAILED(backgroundItem->GetHR()) || failedBackgroundParseItem);
- if (FAILED(backgroundItem->GetHR()))
- {
- if (!failedBackgroundParseItem)
- {
- failedBackgroundParseItem = backgroundItem;
- }
- else
- {
- // If syntax errors are detected on multiple threads, the lexically earlier one should win.
- CompileScriptException *newPse = backgroundItem->GetPSE();
- CompileScriptException *oldPse = failedBackgroundParseItem->GetPSE();
- if (newPse->line < oldPse->line ||
- (newPse->line == oldPse->line && newPse->ichMinLine < oldPse->ichMinLine))
- {
- failedBackgroundParseItem = backgroundItem;
- }
- }
- }
- }
- }
- void BackgroundParser::OnDecommit(JsUtil::ParallelThreadData *threadData)
- {
- if (threadData->parser)
- {
- // Adelete to make sure dtor are called (since the HashTbl has its NoReleaseAllocator)
- Adelete(threadData->threadArena, threadData->parser);
- threadData->parser = nullptr;
- }
- }
- BackgroundParseItem * BackgroundParser::NewBackgroundParseItem(Parser *parser, ParseNodeFnc *parseNode, bool isDeferred)
- {
- BackgroundParseItem *item = Anew(parser->GetAllocator(), BackgroundParseItem, this, parser, parseNode, isDeferred);
- parser->AddBackgroundParseItem(item);
- return item;
- }
- bool BackgroundParser::ParseBackgroundItem(Parser *parser, ParseNodeFnc *parseNode, bool isDeferred)
- {
- ASSERT_THREAD();
- AutoPtr<BackgroundParseItem> workItemAutoPtr(this->NewBackgroundParseItem(parser, parseNode, isDeferred));
- if ((BackgroundParseItem*) workItemAutoPtr == nullptr)
- {
- // OOM, just skip this work item and return.
- // TODO: Raise an OOM parse-time exception.
- return false;
- }
- parser->PrepareForBackgroundParse();
- BackgroundParseItem * backgroundItem = workItemAutoPtr.Detach();
- this->AddToParseQueue(backgroundItem, false, this->Processor()->ProcessesInBackground());
- return true;
- }
- BackgroundParseItem *BackgroundParser::GetJob(BackgroundParseItem *workitem) const
- {
- return workitem;
- }
- bool BackgroundParser::WasAddedToJobProcessor(JsUtil::Job *const job) const
- {
- ASSERT_THREAD();
- Assert(job);
- return static_cast<BackgroundParseItem*>(job)->IsInParseQueue();
- }
- void BackgroundParser::BeforeWaitForJob(BackgroundParseItem *const item) const
- {
- }
- void BackgroundParser::AfterWaitForJob(BackgroundParseItem *const item) const
- {
- }
- void BackgroundParser::AddToParseQueue(BackgroundParseItem *const item, bool prioritize, bool lock)
- {
- AutoOptionalCriticalSection autoLock(lock ? Processor()->GetCriticalSection() : nullptr);
- ++this->pendingBackgroundItems;
- Processor()->AddJob(item, prioritize); // This one can throw (really unlikely though), OOM specifically.
- this->AddUnprocessedItem(item);
- item->OnAddToParseQueue();
- }
- void BackgroundParser::AddUnprocessedItem(BackgroundParseItem *const item)
- {
- if (this->unprocessedItemsTail == nullptr)
- {
- this->unprocessedItemsHead = item;
- }
- else
- {
- this->unprocessedItemsTail->SetNextUnprocessedItem(item);
- }
- item->SetPrevUnprocessedItem(this->unprocessedItemsTail);
- this->unprocessedItemsTail = item;
- }
- void BackgroundParser::RemoveFromUnprocessedItems(BackgroundParseItem *const item)
- {
- if (this->unprocessedItemsHead == item)
- {
- this->unprocessedItemsHead = item->GetNextUnprocessedItem();
- }
- else
- {
- item->GetPrevUnprocessedItem()->SetNextUnprocessedItem(item->GetNextUnprocessedItem());
- }
- if (this->unprocessedItemsTail == item)
- {
- this->unprocessedItemsTail = item->GetPrevUnprocessedItem();
- }
- else
- {
- item->GetNextUnprocessedItem()->SetPrevUnprocessedItem(item->GetPrevUnprocessedItem());
- }
- item->SetNextUnprocessedItem(nullptr);
- item->SetPrevUnprocessedItem(nullptr);
- }
- BackgroundParseItem *BackgroundParser::GetNextUnprocessedItem() const
- {
- BackgroundParseItem *item;
- bool background = this->Processor()->ProcessesInBackground();
- for (item = this->unprocessedItemsHead; item; item = item->GetNextUnprocessedItem())
- {
- if (!background || !static_cast<JsUtil::BackgroundJobProcessor*>(Processor())->IsBeingProcessed(item))
- {
- return item;
- }
- }
- return nullptr;
- }
- BackgroundParseItem::BackgroundParseItem(JsUtil::JobManager *const manager, Parser *const parser, ParseNodeFnc *parseNode, bool defer)
- : JsUtil::Job(manager),
- maxBlockId((uint)-1),
- strictMode(parser->IsStrictMode()),
- parseNode(parseNode),
- parser(nullptr),
- nextItem(nullptr),
- nextUnprocessedItem(nullptr),
- prevUnprocessedItem(nullptr),
- pse(nullptr),
- regExpNodes(nullptr),
- completed(false),
- inParseQueue(false),
- isDeferred(defer)
- {
- parser->CaptureContext(&parseContext);
- }
- void BackgroundParseItem::OnAddToParseQueue()
- {
- this->inParseQueue = true;
- }
- void BackgroundParseItem::OnRemoveFromParseQueue()
- {
- this->inParseQueue = false;
- }
- void BackgroundParseItem::AddRegExpNode(ParseNode *const pnode, ArenaAllocator *alloc)
- {
- if (regExpNodes == nullptr)
- {
- regExpNodes = Anew(alloc, NodeDList, alloc);
- }
- regExpNodes->Append(pnode);
- }
- #endif
- #endif
|