Explorar el Código

Move functionTable deleting to background.
- deleting functionTable along with freeing the JIT code
- while shutting down there's background job to free the JIT code, keep the
function tables in a list and delete before next time codegen and registering
new function tables

This is in order to solve the hang issue while shutting down because
RtlDeleteGrowableFunctionTable is slow.

Lei Shi hace 8 años
padre
commit
8a7ddab91a

+ 4 - 0
Build/Chakra.Core.sln

@@ -11,6 +11,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ChakraCore", "..\bin\Chakra
 		{2F6A1847-BFAF-4B8A-9463-AC39FB46B96A} = {2F6A1847-BFAF-4B8A-9463-AC39FB46B96A}
 		{6979EC58-7A28-465C-A694-F3323A1F5401} = {6979EC58-7A28-465C-A694-F3323A1F5401}
 		{F6FAD160-5A4B-476A-93AC-33E0B3A18C0C} = {F6FAD160-5A4B-476A-93AC-33E0B3A18C0C}
+		{F48B3491-81DF-4F49-B35F-3308CBE6A379} = {F48B3491-81DF-4F49-B35F-3308CBE6A379}
 		{18CF279F-188D-4655-B03D-74F65388E7D1} = {18CF279F-188D-4655-B03D-74F65388E7D1}
 		{ABC904AD-9415-46F8-AA23-E33193F81F7C} = {ABC904AD-9415-46F8-AA23-E33193F81F7C}
 		{8C61E4E7-F0D6-420D-A352-3E6E50D406DD} = {8C61E4E7-F0D6-420D-A352-3E6E50D406DD}
@@ -755,4 +756,7 @@ Global
 		{02D4FD92-AD34-40CA-85DF-4D6C7E3A1F22} = {546172B2-F084-4363-BE35-06010663D319}
 		{F48B3491-81DF-4F49-B35F-3308CBE6A379} = {D8216B93-BD6E-4293-8D98-79CEF7CF66BC}
 	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {1F6CA1BC-6C01-4C82-8505-6A7690EBD556}
+	EndGlobalSection
 EndGlobal

+ 1 - 1
lib/Backend/CodeGenWorkItem.cpp

@@ -205,7 +205,7 @@ void CodeGenWorkItem::OnWorkItemProcessFail(NativeCodeGenerator* codeGen)
 #if DBG
         this->allocation->allocation->isNotExecutableBecauseOOM = true;
 #endif
-        codeGen->FreeNativeCodeGenAllocation(this->allocation->allocation->address);
+        codeGen->FreeNativeCodeGenAllocation(this->allocation->allocation->address, nullptr);
     }
 }
 

+ 9 - 1
lib/Backend/EmitBuffer.cpp

@@ -191,7 +191,7 @@ EmitBufferManager<TAlloc, TPreReservedAlloc, SyncObject>::NewAllocation(size_t b
 
 template <typename TAlloc, typename TPreReservedAlloc, class SyncObject>
 bool
-EmitBufferManager<TAlloc, TPreReservedAlloc, SyncObject>::FreeAllocation(void* address)
+EmitBufferManager<TAlloc, TPreReservedAlloc, SyncObject>::FreeAllocation(void* address, void** functionTable)
 {
     AutoRealOrFakeCriticalSection<SyncObject> autoCs(&this->criticalSection);
 #if _M_ARM
@@ -239,6 +239,14 @@ EmitBufferManager<TAlloc, TPreReservedAlloc, SyncObject>::FreeAllocation(void* a
             }
             VerboseHeapTrace(_u("Freeing 0x%p, allocation: 0x%p\n"), address, allocation->allocation->address);
 
+#if PDATA_ENABLED && defined(_WIN32)
+            if (functionTable && *functionTable)
+            {
+                NtdllLibrary::Instance->DeleteGrowableFunctionTable(*functionTable);
+                *functionTable = nullptr;
+            }
+#endif
+
             this->allocationHeap.Free(allocation->allocation);
             this->allocator->Free(allocation, sizeof(TEmitBufferAllocation));
 

+ 1 - 1
lib/Backend/EmitBuffer.h

@@ -46,7 +46,7 @@ public:
     bool ProtectBufferWithExecuteReadWriteForInterpreter(TEmitBufferAllocation* allocation);
     bool CommitBufferForInterpreter(TEmitBufferAllocation* allocation, _In_reads_bytes_(bufferSize) BYTE* pBuffer, _In_ size_t bufferSize);
     void CompletePreviousAllocation(TEmitBufferAllocation* allocation);
-    bool FreeAllocation(void* address);
+    bool FreeAllocation(void* address, void** functionTable);
     //Ends here
 
     bool IsInHeap(void* address);

+ 8 - 0
lib/Backend/InterpreterThunkEmitter.cpp

@@ -323,6 +323,10 @@ void* InterpreterThunkEmitter::ConvertToEntryPoint(PVOID dynamicInterpreterThunk
 
 bool InterpreterThunkEmitter::NewThunkBlock()
 {
+    // flush the function tables before allocating any new  code
+    // to prevent old function table is referring to new code address
+    DelayDeletingFunctionTable::Clear();
+
 #ifdef ENABLE_OOP_NATIVE_CODEGEN
     if (CONFIG_FLAG(ForceStaticInterpreterThunk))
     {
@@ -392,6 +396,10 @@ bool InterpreterThunkEmitter::NewThunkBlock()
 #ifdef ENABLE_OOP_NATIVE_CODEGEN
 bool InterpreterThunkEmitter::NewOOPJITThunkBlock()
 {
+    // flush the function tables before allocating any new  code
+    // to prevent old function table is referring to new code address
+    DelayDeletingFunctionTable::Clear();
+
     PSCRIPTCONTEXT_HANDLE remoteScriptContext = this->scriptContext->GetRemoteScriptAddr();
     if (!JITManager::GetJITManager()->IsConnected())
     {

+ 26 - 11
lib/Backend/NativeCodeGenerator.cpp

@@ -892,6 +892,10 @@ void NativeCodeGenerator::CodeGen(PageAllocator* pageAllocator, CodeGenWorkItemI
 void
 NativeCodeGenerator::CodeGen(PageAllocator * pageAllocator, CodeGenWorkItem* workItem, const bool foreground)
 {
+    // flush the function tables before allocating any new  code
+    // to prevent old function table is referring to new code address
+    DelayDeletingFunctionTable::Clear();
+
     if(foreground)
     {
         // Func::Codegen has a lot of things on the stack, so probe the stack here instead
@@ -3200,14 +3204,14 @@ NativeCodeGenerator::EnterScriptStart()
 }
 
 void
-FreeNativeCodeGenAllocation(Js::ScriptContext *scriptContext, Js::JavascriptMethod codeAddress, Js::JavascriptMethod thunkAddress)
+FreeNativeCodeGenAllocation(Js::ScriptContext *scriptContext, Js::JavascriptMethod codeAddress, Js::JavascriptMethod thunkAddress, void** functionTable)
 {
     if (!scriptContext->GetNativeCodeGenerator())
-    {
+    { 
         return;
     }
 
-    scriptContext->GetNativeCodeGenerator()->QueueFreeNativeCodeGenAllocation((void*)codeAddress, (void*)thunkAddress);
+    scriptContext->GetNativeCodeGenerator()->QueueFreeNativeCodeGenAllocation((void*)codeAddress, (void*)thunkAddress, functionTable);
 }
 
 bool TryReleaseNonHiPriWorkItem(Js::ScriptContext* scriptContext, CodeGenWorkItem* workItem)
@@ -3238,22 +3242,30 @@ bool NativeCodeGenerator::TryReleaseNonHiPriWorkItem(CodeGenWorkItem* workItem)
 }
 
 void
-NativeCodeGenerator::FreeNativeCodeGenAllocation(void* codeAddress)
+NativeCodeGenerator::FreeNativeCodeGenAllocation(void* codeAddress, void** functionTable)
 {
     if (JITManager::GetJITManager()->IsOOPJITEnabled())
     {
+        // function table delete in content process
+#if PDATA_ENABLED && defined(_WIN32)
+        if (functionTable && *functionTable)
+        {
+            NtdllLibrary::Instance->DeleteGrowableFunctionTable(*functionTable);
+            *functionTable = nullptr;
+        }
+#endif
         ThreadContext * context = this->scriptContext->GetThreadContext();
         HRESULT hr = JITManager::GetJITManager()->FreeAllocation(context->GetRemoteThreadContextAddr(), (intptr_t)codeAddress);
         JITManager::HandleServerCallResult(hr, RemoteCallType::MemFree);
     }
     else if(this->backgroundAllocators)
     {
-        this->backgroundAllocators->emitBufferManager.FreeAllocation(codeAddress);
+        this->backgroundAllocators->emitBufferManager.FreeAllocation(codeAddress, functionTable);
     }
 }
 
 void
-NativeCodeGenerator::QueueFreeNativeCodeGenAllocation(void* codeAddress, void * thunkAddress)
+NativeCodeGenerator::QueueFreeNativeCodeGenAllocation(void* codeAddress, void * thunkAddress, void** functionTable)
 {
     ASSERT_THREAD();
 
@@ -3283,24 +3295,24 @@ NativeCodeGenerator::QueueFreeNativeCodeGenAllocation(void* codeAddress, void *
     // OOP JIT will always queue a job
 
     // The foreground allocators may have been used
-    if(this->foregroundAllocators && this->foregroundAllocators->emitBufferManager.FreeAllocation(codeAddress))
+    if (this->foregroundAllocators && this->foregroundAllocators->emitBufferManager.FreeAllocation(codeAddress, functionTable))
     {
         return;
     }
 
     // The background allocators were used. Queue a job to free the allocation from the background thread.
-    this->freeLoopBodyManager.QueueFreeLoopBodyJob(codeAddress, thunkAddress);
+    this->freeLoopBodyManager.QueueFreeLoopBodyJob(codeAddress, thunkAddress, functionTable);
 }
 
-void NativeCodeGenerator::FreeLoopBodyJobManager::QueueFreeLoopBodyJob(void* codeAddress, void * thunkAddress)
+void NativeCodeGenerator::FreeLoopBodyJobManager::QueueFreeLoopBodyJob(void* codeAddress, void * thunkAddress, void** functionTable)
 {
     Assert(!this->isClosed);
 
-    FreeLoopBodyJob* job = HeapNewNoThrow(FreeLoopBodyJob, this, codeAddress, thunkAddress);
+    FreeLoopBodyJob* job = HeapNewNoThrow(FreeLoopBodyJob, this, codeAddress, thunkAddress, *functionTable);
 
     if (job == nullptr)
     {
-        FreeLoopBodyJob stackJob(this, codeAddress, thunkAddress, false /* heapAllocated */);
+        FreeLoopBodyJob stackJob(this, codeAddress, thunkAddress, *functionTable, false /* heapAllocated */);
 
         {
             AutoOptionalCriticalSection lock(Processor()->GetCriticalSection());
@@ -3324,6 +3336,9 @@ void NativeCodeGenerator::FreeLoopBodyJobManager::QueueFreeLoopBodyJob(void* cod
             HeapDelete(job);
         }
     }
+
+    // function table successfully transferred to background job
+    *functionTable = nullptr;
 }
 
 #ifdef PROFILE_EXEC

+ 8 - 5
lib/Backend/NativeCodeGenerator.h

@@ -104,10 +104,10 @@ public:
     void UpdateQueueForDebugMode();
     bool IsBackgroundJIT() const;
     void EnterScriptStart();
-    void FreeNativeCodeGenAllocation(void* codeAddress);
+    void FreeNativeCodeGenAllocation(void* codeAddress, void** functionTable);
     bool TryReleaseNonHiPriWorkItem(CodeGenWorkItem* workItem);
 
-    void QueueFreeNativeCodeGenAllocation(void* codeAddress, void* thunkAddress);
+    void QueueFreeNativeCodeGenAllocation(void* codeAddress, void* thunkAddress, void** functionTable);
 
     bool IsClosed() { return isClosed; }
     void AddWorkItem(CodeGenWorkItem* workItem);
@@ -201,10 +201,11 @@ private:
     class FreeLoopBodyJob: public JsUtil::Job
     {
     public:
-        FreeLoopBodyJob(JsUtil::JobManager *const manager, void* codeAddress, void* thunkAddress, bool isHeapAllocated = true):
+        FreeLoopBodyJob(JsUtil::JobManager *const manager, void* codeAddress, void* thunkAddress, void* functionTable, bool isHeapAllocated = true):
           JsUtil::Job(manager),
           codeAddress(codeAddress),
           thunkAddress(thunkAddress),
+          functionTable(functionTable),
           heapAllocated(isHeapAllocated)
         {
         }
@@ -212,6 +213,7 @@ private:
         bool heapAllocated;
         void* codeAddress;
         void* thunkAddress;
+        void* functionTable;
     };
 
     class FreeLoopBodyJobManager sealed: public WaitableJobManager
@@ -279,8 +281,9 @@ private:
         {
             FreeLoopBodyJob* freeLoopBodyJob = static_cast<FreeLoopBodyJob*>(job);
 
+            void* functionTable = freeLoopBodyJob->functionTable;
             // Free Loop Body
-            nativeCodeGen->FreeNativeCodeGenAllocation(freeLoopBodyJob->codeAddress);
+            nativeCodeGen->FreeNativeCodeGenAllocation(freeLoopBodyJob->codeAddress, &functionTable);
 
             return true;
         }
@@ -303,7 +306,7 @@ private:
             }
         }
 
-        void QueueFreeLoopBodyJob(void* codeAddress, void* thunkAddress);
+        void QueueFreeLoopBodyJob(void* codeAddress, void* thunkAddress, void** functionTable);
 
     private:
         NativeCodeGenerator* nativeCodeGen;

+ 1 - 0
lib/Backend/PDataManager.cpp

@@ -45,6 +45,7 @@ void PDataManager::UnregisterPdata(RUNTIME_FUNCTION* pdata)
 {
     if (AutoSystemInfo::Data.IsWin8OrLater())
     {
+        // TODO: need to move to background?
         NtdllLibrary::Instance->DeleteGrowableFunctionTable(pdata);
     }
     else

+ 1 - 1
lib/Common/BackendApi.h

@@ -62,7 +62,7 @@ void UpdateNativeCodeGeneratorForDebugMode(NativeCodeGenerator* nativeCodeGen);
 CriticalSection *GetNativeCodeGenCriticalSection(NativeCodeGenerator *pNativeCodeGen);
 bool TryReleaseNonHiPriWorkItem(Js::ScriptContext* scriptContext, CodeGenWorkItem* workItem);
 void NativeCodeGenEnterScriptStart(NativeCodeGenerator * nativeCodeGen);
-void FreeNativeCodeGenAllocation(Js::ScriptContext* scriptContext, Js::JavascriptMethod codeAddress, Js::JavascriptMethod thunkAddress);
+void FreeNativeCodeGenAllocation(Js::ScriptContext* scriptContext, Js::JavascriptMethod codeAddress, Js::JavascriptMethod thunkAddress, void** functionTable);
 InProcCodeGenAllocators* GetForegroundAllocator(NativeCodeGenerator * nativeCodeGen, PageAllocator* pageallocator);
 void GenerateFunction(NativeCodeGenerator * nativeCodeGen, Js::FunctionBody * functionBody, Js::ScriptFunction * function = NULL);
 void GenerateLoopBody(NativeCodeGenerator * nativeCodeGen, Js::FunctionBody * functionBody, Js::LoopHeader * loopHeader, Js::EntryPointInfo* entryPointInfo, uint localCount, Js::Var localSlots[]);

+ 7 - 0
lib/Common/Common/Jobs.cpp

@@ -27,6 +27,7 @@
 #include "Common/Jobs.inl"
 #include "Core/CommonMinMax.h"
 #include "Memory/RecyclerWriteBarrierManager.h"
+#include "Memory/XDataAllocator.h"
 
 namespace JsUtil
 {
@@ -681,6 +682,9 @@ namespace JsUtil
             if (threadData->CanDecommit())
             {
                 // If its 1sec time out decommit and wait for INFINITE
+                // flush the function tables in background while idle
+                DelayDeletingFunctionTable::Clear();
+
                 threadData->backgroundPageAllocator.DecommitNow();
                 this->ForEachManager([&](JobManager *manager){
                     manager->OnDecommit(threadData);
@@ -1103,6 +1107,9 @@ namespace JsUtil
             }
             criticalSection.Leave();
 
+            // flush the function tables in background thread after closed and before shutting down thread
+            DelayDeletingFunctionTable::Clear();
+
             EDGE_ETW_INTERNAL(EventWriteJSCRIPT_NATIVECODEGEN_STOP(this, 0));
         }
     }

+ 2 - 0
lib/Common/Core/DelayLoadLibrary.cpp

@@ -70,6 +70,7 @@ DWORD NtdllLibrary::AddGrowableFunctionTable( _Out_ PVOID * DynamicTable,
     _In_ ULONG_PTR RangeBase,
     _In_ ULONG_PTR RangeEnd )
 {
+    Assert(AutoSystemInfo::Data.IsWin8OrLater());
     if(m_hModule)
     {
         if(addGrowableFunctionTable == NULL)
@@ -93,6 +94,7 @@ DWORD NtdllLibrary::AddGrowableFunctionTable( _Out_ PVOID * DynamicTable,
 
 VOID NtdllLibrary::DeleteGrowableFunctionTable( _In_ PVOID DynamicTable )
 {
+    Assert(AutoSystemInfo::Data.IsWin8OrLater());
     if(m_hModule)
     {
         if(deleteGrowableFunctionTable == NULL)

+ 78 - 6
lib/Common/Memory/CustomHeap.cpp

@@ -3,16 +3,88 @@
 // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
 //-------------------------------------------------------------------------------------------------------
 #include "CommonMemoryPch.h"
-#ifdef _M_X64
-#include "Memory/amd64/XDataAllocator.h"
-#elif defined(_M_ARM)
-#include "Memory/arm/XDataAllocator.h"
+#include "Memory/XDataAllocator.h"
+#if defined(_M_ARM)
 #include <wchar.h>
-#elif defined(_M_ARM64)
-#include "Memory/arm64/XDataAllocator.h"
 #endif
 #include "CustomHeap.h"
 
+#if PDATA_ENABLED && defined(_WIN32)
+#include "Core/DelayLoadLibrary.h"
+#include <malloc.h>
+#endif
+
+PSLIST_HEADER DelayDeletingFunctionTable::Head = nullptr;
+DelayDeletingFunctionTable DelayDeletingFunctionTable::Instance;
+
+DelayDeletingFunctionTable::DelayDeletingFunctionTable()
+{
+#if PDATA_ENABLED && defined(_WIN32)
+    Head = (PSLIST_HEADER)_aligned_malloc(sizeof(SLIST_HEADER), MEMORY_ALLOCATION_ALIGNMENT);
+    if (Head)
+    {
+        InitializeSListHead(Head);
+    }
+#endif
+}
+DelayDeletingFunctionTable::~DelayDeletingFunctionTable()
+{
+#if PDATA_ENABLED && defined(_WIN32)
+    Clear();
+    if (Head)
+    {
+        DebugOnly(SLIST_ENTRY* entry = InterlockedPopEntrySList(Head));
+        Assert(entry == nullptr);
+        _aligned_free(Head);
+        Head = nullptr;
+    }
+#endif
+}
+
+
+bool DelayDeletingFunctionTable::AddEntry(FunctionTableHandle ft)
+{
+#if PDATA_ENABLED && defined(_WIN32)
+    if (Head)
+    {
+        FunctionTableNode* node = (FunctionTableNode*)_aligned_malloc(sizeof(FunctionTableNode), MEMORY_ALLOCATION_ALIGNMENT);
+        if (node)
+        {
+            node->functionTable = ft;
+            InterlockedPushEntrySList(Head, &(node->itemEntry));
+            return true;
+        }
+    }
+#endif
+    return false;
+}
+
+void DelayDeletingFunctionTable::Clear()
+{
+#if PDATA_ENABLED && defined(_WIN32)
+    if (Head)
+    {
+        SLIST_ENTRY* entry = InterlockedPopEntrySList(Head);
+        while (entry)
+        {
+            FunctionTableNode* list = (FunctionTableNode*)entry;
+            NtdllLibrary::Instance->DeleteGrowableFunctionTable(list->functionTable);
+            _aligned_free(entry);
+            entry = InterlockedPopEntrySList(Head);
+        }
+    }
+#endif
+}
+
+bool DelayDeletingFunctionTable::IsEmpty()
+{
+#if PDATA_ENABLED && defined(_WIN32)
+    return QueryDepthSList(Head) == 0;
+#else
+    return true;
+#endif
+}
+
 namespace Memory
 {
 namespace CustomHeap

+ 22 - 0
lib/Common/Memory/XDataAllocator.h

@@ -11,3 +11,25 @@
 #elif defined(_M_ARM64)
 #include "Memory/arm64/XDataAllocator.h"
 #endif
+
+struct FunctionTableNode
+{
+#ifdef _WIN32
+    SLIST_ENTRY itemEntry;
+    FunctionTableHandle functionTable;
+
+#endif
+};
+
+struct DelayDeletingFunctionTable
+{
+    static PSLIST_HEADER Head;
+    static DelayDeletingFunctionTable Instance;
+
+    DelayDeletingFunctionTable();
+    ~DelayDeletingFunctionTable();
+
+    static bool AddEntry(FunctionTableHandle ft);
+    static void Clear();
+    static bool IsEmpty();
+};

+ 1 - 6
lib/Common/Memory/amd64/XDataAllocator.cpp

@@ -162,12 +162,7 @@ void XDataAllocator::Register(XDataAllocation * xdataInfo, ULONG_PTR functionSta
 void XDataAllocator::Unregister(XDataAllocation * xdataInfo)
 {
 #ifdef _WIN32
-    // Delete the table
-    if (AutoSystemInfo::Data.IsWin8OrLater())
-    {
-        NtdllLibrary::Instance->DeleteGrowableFunctionTable(xdataInfo->functionTable);
-    }
-    else
+    if (!AutoSystemInfo::Data.IsWin8OrLater())
     {
         BOOLEAN success = RtlDeleteFunctionTable(&xdataInfo->pdata);
         Assert(success);

+ 3 - 0
lib/Common/Memory/amd64/XDataAllocator.h

@@ -18,6 +18,9 @@ namespace Memory
 struct XDataAllocation : public SecondaryAllocation
 {
     XDataAllocation()
+#ifdef _WIN32
+        :functionTable(nullptr)
+#endif
     {}
 
     bool IsFreed() const

+ 1 - 3
lib/Common/Memory/arm/XDataAllocator.cpp

@@ -99,9 +99,7 @@ void XDataAllocator::Register(XDataAllocation * xdataInfo, DWORD functionStart,
 /* static */
 void XDataAllocator::Unregister(XDataAllocation * xdataInfo)
 {
-#ifdef _WIN32
-    NtdllLibrary::Instance->DeleteGrowableFunctionTable(xdataInfo->functionTable);
-#else  // !_WIN32
+#ifndef _WIN32
     Assert(ReadHead(xdataInfo->address));  // should be non-empty .eh_frame
     __DEREGISTER_FRAME(xdataInfo->address);
 #endif

+ 1 - 4
lib/Common/Memory/arm64/XDataAllocator.cpp

@@ -154,10 +154,7 @@ void XDataAllocator::Register(XDataAllocation * xdataInfo, ULONG_PTR functionSta
 /* static */
 void XDataAllocator::Unregister(XDataAllocation * xdataInfo)
 {
-#ifdef _WIN32
-    // Delete the table
-    NtdllLibrary::Instance->DeleteGrowableFunctionTable(xdataInfo->functionTable);
-#else  // !_WIN32
+#ifndef _WIN32
     Assert(ReadHead(xdataInfo->address));  // should be non-empty .eh_frame
     __DEREGISTER_FRAME(xdataInfo->address);
 #endif

+ 3 - 0
lib/Common/Memory/arm64/XDataAllocator.h

@@ -19,6 +19,9 @@ namespace Memory
     struct XDataAllocation : public SecondaryAllocation
     {
         XDataAllocation()
+#ifdef _WIN32
+            :functionTable(nullptr)
+#endif
         {}
 
         bool IsFreed() const

+ 1 - 1
lib/JITServer/JITServer.cpp

@@ -672,7 +672,7 @@ ServerFreeAllocation(
 
     return ServerCallWrapper(context, [&]()->HRESULT
     {
-        context->GetCodeGenAllocators()->emitBufferManager.FreeAllocation((void*)codeAddress);
+        context->GetCodeGenAllocators()->emitBufferManager.FreeAllocation((void*)codeAddress, nullptr);
         return S_OK;
     });
 }

+ 35 - 43
lib/Runtime/Base/FunctionBody.cpp

@@ -8830,31 +8830,38 @@ namespace Js
     {
         if (this->GetState() != CleanedUp)
         {
-            // Unregister xdataInfo before OnCleanup() which may release xdataInfo->address
 #if ENABLE_NATIVE_CODEGEN
-#if defined(TARGET_64)
-            if (this->xdataInfo != nullptr)
-            {
-                XDataAllocator::Unregister(this->xdataInfo);
-                HeapDelete(this->xdataInfo);
-                this->xdataInfo = nullptr;
-            }
-#elif defined(_M_ARM)
+            void* functionTable = nullptr;
+#if PDATA_ENABLED
             if (this->xdataInfo != nullptr)
             {
+#ifdef _WIN32
+                functionTable = this->xdataInfo->functionTable;
+#endif
                 XDataAllocator::Unregister(this->xdataInfo);
+#if defined(_M_ARM32_OR_ARM64)
                 if (JITManager::GetJITManager()->IsOOPJITEnabled())
+#endif
                 {
                     HeapDelete(this->xdataInfo);
                 }
                 this->xdataInfo = nullptr;
             }
-#endif
 #endif
 
-            this->OnCleanup(isShutdown);
+            this->OnCleanup(isShutdown, &functionTable);
+
+#if PDATA_ENABLED && defined(_WIN32)
+            // functionTable is not transferred somehow, delete in-thread
+            if (functionTable)
+            {
+                if (!DelayDeletingFunctionTable::AddEntry(functionTable))
+                {
+                    NtdllLibrary::Instance->DeleteGrowableFunctionTable(functionTable);
+                }
+            }
+#endif
 
-#if ENABLE_NATIVE_CODEGEN
             FreeJitTransferData();
 
             if (this->bailoutRecordMap != nullptr)
@@ -8976,7 +8983,15 @@ namespace Js
         // Reset the entry point upon a lazy bailout.
         this->Reset(true);
         Assert(this->nativeAddress != nullptr);
-        FreeNativeCodeGenAllocation(GetScriptContext(), this->nativeAddress, this->thunkAddress);
+
+        void* functionTable = nullptr;
+#if PDATA_ENABLED && defined(_WIN32)
+        if (this->xdataInfo)
+        {
+            functionTable = this->xdataInfo->functionTable;
+        }
+#endif
+        FreeNativeCodeGenAllocation(GetScriptContext(), this->nativeAddress, this->thunkAddress, &functionTable);
         this->nativeAddress = nullptr;
         this->jsMethod = nullptr;
     }
@@ -9072,7 +9087,7 @@ namespace Js
         return functionProxy->GetFunctionBody();
     }
 
-    void FunctionEntryPointInfo::OnCleanup(bool isShutdown)
+    void FunctionEntryPointInfo::OnCleanup(bool isShutdown, void** functionTable)
     {
         if (this->IsCodeGenDone())
         {
@@ -9083,19 +9098,7 @@ namespace Js
                 HeapDelete(this->inlineeFrameMap);
                 this->inlineeFrameMap = nullptr;
             }
-#if PDATA_ENABLED
-            if (this->xdataInfo != nullptr)
-            {
-                XDataAllocator::Unregister(this->xdataInfo);
-#if defined(_M_ARM32_OR_ARM64)
-                if (JITManager::GetJITManager()->IsOOPJITEnabled())
-#endif
-                {
-                    HeapDelete(this->xdataInfo);
-                }
-                this->xdataInfo = nullptr;
-            }
-#endif
+
 #endif
 
             if(nativeEntryPointProcessed)
@@ -9146,7 +9149,8 @@ namespace Js
 
                 if (validationCookie == currentCookie)
                 {
-                    scriptContext->FreeFunctionEntryPoint((Js::JavascriptMethod)this->GetNativeAddress(), this->GetThunkAddress());
+                    scriptContext->FreeFunctionEntryPoint((Js::JavascriptMethod)this->GetNativeAddress(), this->GetThunkAddress(), functionTable);
+                    *functionTable = nullptr;
                 }
             }
 
@@ -9406,7 +9410,7 @@ namespace Js
 
     //End AsmJs Support
 
-    void LoopEntryPointInfo::OnCleanup(bool isShutdown)
+    void LoopEntryPointInfo::OnCleanup(bool isShutdown, void** functionTable)
     {
 #ifdef ASMJS_PLAT
         if (this->IsCodeGenDone() && !this->GetIsTJMode())
@@ -9423,19 +9427,6 @@ namespace Js
                 HeapDelete(this->inlineeFrameMap);
                 this->inlineeFrameMap = nullptr;
             }
-#if PDATA_ENABLED
-            if (this->xdataInfo != nullptr)
-            {
-                XDataAllocator::Unregister(this->xdataInfo);
-#if defined(_M_ARM32_OR_ARM64)
-                if (JITManager::GetJITManager()->IsOOPJITEnabled())
-#endif
-                {
-                    HeapDelete(this->xdataInfo);
-                }
-                this->xdataInfo = nullptr;
-            }
-#endif
 #endif
 
             if (!isShutdown)
@@ -9467,7 +9458,8 @@ namespace Js
 
                 if (validationCookie == currentCookie)
                 {
-                    scriptContext->FreeFunctionEntryPoint(reinterpret_cast<Js::JavascriptMethod>(this->GetNativeAddress()), this->GetThunkAddress());
+                    scriptContext->FreeFunctionEntryPoint(reinterpret_cast<Js::JavascriptMethod>(this->GetNativeAddress()), this->GetThunkAddress(), functionTable);
+                    *functionTable = nullptr;
                 }
             }
 

+ 3 - 3
lib/Runtime/Base/FunctionBody.h

@@ -485,7 +485,7 @@ namespace Js
 
         virtual void ReleasePendingWorkItem() {};
 
-        virtual void OnCleanup(bool isShutdown) = 0;
+        virtual void OnCleanup(bool isShutdown, void** functionTable) = 0;
 
 #ifdef PERF_COUNTERS
         virtual void OnRecorded() = 0;
@@ -907,7 +907,7 @@ namespace Js
         }
 #endif
 
-        virtual void OnCleanup(bool isShutdown) override;
+        virtual void OnCleanup(bool isShutdown, void** functionTable) override;
 
         virtual void ReleasePendingWorkItem() override;
 
@@ -936,7 +936,7 @@ namespace Js
 
         virtual FunctionBody *GetFunctionBody() const override;
 
-        virtual void OnCleanup(bool isShutdown) override;
+        virtual void OnCleanup(bool isShutdown, void** functionTable) override;
 
 #if ENABLE_NATIVE_CODEGEN
         virtual void ResetOnNativeCodeInstallFailure() override;

+ 2 - 2
lib/Runtime/Base/ScriptContext.cpp

@@ -4390,10 +4390,10 @@ namespace Js
     }
 #endif
 
-    void ScriptContext::FreeFunctionEntryPoint(Js::JavascriptMethod codeAddress, Js::JavascriptMethod thunkAddress)
+    void ScriptContext::FreeFunctionEntryPoint(Js::JavascriptMethod codeAddress, Js::JavascriptMethod thunkAddress, void** functionTable)
     {
 #if ENABLE_NATIVE_CODEGEN
-        FreeNativeCodeGenAllocation(this, codeAddress, thunkAddress);
+        FreeNativeCodeGenAllocation(this, codeAddress, thunkAddress, functionTable);
 #endif
     }
 

+ 1 - 1
lib/Runtime/Base/ScriptContext.h

@@ -1438,7 +1438,7 @@ private:
             return threadContext->GetEmptyStringPropertyId();
         }
 
-        void FreeFunctionEntryPoint(Js::JavascriptMethod codeAddress, Js::JavascriptMethod thunkAddress);
+        void FreeFunctionEntryPoint(Js::JavascriptMethod codeAddress, Js::JavascriptMethod thunkAddress, void** functionTable);
 
     private:
         uint CloneSource(Utf8SourceInfo* info);