diff --git a/lib/Runtime/Base/FunctionBody.cpp b/lib/Runtime/Base/FunctionBody.cpp index 8166b1e41f8..c3e63edf430 100644 --- a/lib/Runtime/Base/FunctionBody.cpp +++ b/lib/Runtime/Base/FunctionBody.cpp @@ -7170,13 +7170,11 @@ namespace Js } } - if (!IsScriptContextShutdown) + if (unregisteredInlineCacheCount > 0) { + AssertMsg(!IsScriptContextShutdown, "Unregistration of inlineCache should only be done if this is not scriptContext shutdown."); ThreadContext* threadContext = this->m_scriptContext->GetThreadContext(); - if (unregisteredInlineCacheCount > 0) - { - threadContext->NotifyInlineCacheBatchUnregistered(unregisteredInlineCacheCount); - } + threadContext->NotifyInlineCacheBatchUnregistered(unregisteredInlineCacheCount); } while (this->GetPolymorphicInlineCachesHead()) diff --git a/lib/Runtime/Base/ThreadContext.cpp b/lib/Runtime/Base/ThreadContext.cpp index a954423bd7a..6ed5c321e3f 100644 --- a/lib/Runtime/Base/ThreadContext.cpp +++ b/lib/Runtime/Base/ThreadContext.cpp @@ -2865,6 +2865,7 @@ ThreadContext::InvalidateAndDeleteInlineCacheList(InlineCacheList* inlineCacheLi Assert(inlineCacheList != nullptr); uint cacheCount = 0; + uint nullCacheCount = 0; FOREACH_SLISTBASE_ENTRY(Js::InlineCache*, inlineCache, inlineCacheList) { cacheCount++; @@ -2878,15 +2879,24 @@ ThreadContext::InvalidateAndDeleteInlineCacheList(InlineCacheList* inlineCacheLi memset(inlineCache, 0, sizeof(Js::InlineCache)); } + else + { + nullCacheCount++; + } } NEXT_SLISTBASE_ENTRY; Adelete(&this->inlineCacheThreadInfoAllocator, inlineCacheList); this->registeredInlineCacheCount = this->registeredInlineCacheCount > cacheCount ? this->registeredInlineCacheCount - cacheCount : 0; + this->unregisteredInlineCacheCount = this->unregisteredInlineCacheCount > nullCacheCount ? this->unregisteredInlineCacheCount - nullCacheCount : 0; } void ThreadContext::CompactInlineCacheInvalidationLists() { +#if DBG + uint countOfNodesToCompact = this->unregisteredInlineCacheCount; + this->totalUnregisteredCacheCount = 0; +#endif Assert(this->unregisteredInlineCacheCount > 0); CompactProtoInlineCaches(); @@ -2894,6 +2904,7 @@ ThreadContext::CompactInlineCacheInvalidationLists() { CompactStoreFieldInlineCaches(); } + Assert(countOfNodesToCompact == this->totalUnregisteredCacheCount); } void @@ -2931,10 +2942,18 @@ ThreadContext::CompactInlineCacheList(InlineCacheList* inlineCacheList) } NEXT_SLISTBASE_ENTRY_EDITING; +#if DBG + this->totalUnregisteredCacheCount += cacheCount; +#endif if (cacheCount > 0) { + AssertMsg(this->unregisteredInlineCacheCount >= cacheCount, "Some codepaths didn't unregistered the inlineCaches which might leak memory."); this->unregisteredInlineCacheCount = this->unregisteredInlineCacheCount > cacheCount ? this->unregisteredInlineCacheCount - cacheCount : 0; + + AssertMsg(this->registeredInlineCacheCount >= cacheCount, "Some codepaths didn't registered the inlineCaches which might leak memory."); + this->registeredInlineCacheCount = this->registeredInlineCacheCount > cacheCount ? + this->registeredInlineCacheCount - cacheCount : 0; } } diff --git a/lib/Runtime/Base/ThreadContext.h b/lib/Runtime/Base/ThreadContext.h index 907048f39e0..85ad9dff092 100644 --- a/lib/Runtime/Base/ThreadContext.h +++ b/lib/Runtime/Base/ThreadContext.h @@ -718,6 +718,9 @@ class ThreadContext sealed : uint registeredInlineCacheCount; uint unregisteredInlineCacheCount; +#if DBG + uint totalUnregisteredCacheCount; +#endif typedef JsUtil::BaseDictionary IsInstInlineCacheListMapByFunction; IsInstInlineCacheListMapByFunction isInstInlineCacheByFunction; diff --git a/lib/Runtime/Language/JavascriptOperators.cpp b/lib/Runtime/Language/JavascriptOperators.cpp index a0f7483eadc..b45ffd945da 100644 --- a/lib/Runtime/Language/JavascriptOperators.cpp +++ b/lib/Runtime/Language/JavascriptOperators.cpp @@ -9374,6 +9374,8 @@ namespace Js return; } + uint unregisteredInlineCacheCount = 0; + Assert(inlineCaches && size > 0); // If we're not shutting down (as in closing the script context), we need to remove our inline caches from @@ -9390,7 +9392,10 @@ namespace Js { for (int i = 0; i < size; i++) { - inlineCaches[i].RemoveFromInvalidationList(); + if (inlineCaches[i].RemoveFromInvalidationList()) + { + unregisteredInlineCacheCount++; + } } AllocatorDeleteArray(InlineCacheAllocator, functionBody->GetScriptContext()->GetInlineCacheAllocator(), size, inlineCaches); @@ -9426,6 +9431,10 @@ namespace Js prev = next = nullptr; inlineCaches = nullptr; size = 0; + if (unregisteredInlineCacheCount > 0) + { + functionBody->GetScriptContext()->GetThreadContext()->NotifyInlineCacheBatchUnregistered(unregisteredInlineCacheCount); + } } JavascriptString * JavascriptOperators::Concat3(Var aLeft, Var aCenter, Var aRight, ScriptContext * scriptContext) diff --git a/lib/Runtime/Library/RootObjectBase.cpp b/lib/Runtime/Library/RootObjectBase.cpp index 6b4d65272c9..2cd4726af10 100644 --- a/lib/Runtime/Library/RootObjectBase.cpp +++ b/lib/Runtime/Library/RootObjectBase.cpp @@ -99,11 +99,13 @@ namespace Js void RootObjectBase::ReleaseInlineCache(Js::PropertyId propertyId, bool isLoadMethod, bool isStore, bool isShutdown) { + uint unregisteredInlineCacheCount = 0; + RootObjectInlineCacheMap * inlineCacheMap = isStore ? storeInlineCacheMap : isLoadMethod ? loadMethodInlineCacheMap : loadInlineCacheMap; bool found = false; inlineCacheMap->RemoveIfWithKey(propertyId, - [this, isShutdown, &found](PropertyRecord const * propertyRecord, RootObjectInlineCache * rootObjectInlineCache) + [this, isShutdown, &unregisteredInlineCacheCount, &found](PropertyRecord const * propertyRecord, RootObjectInlineCache * rootObjectInlineCache) { found = true; if (rootObjectInlineCache->Release() == 0) @@ -114,7 +116,11 @@ namespace Js // Close called) and thus the invalidation lists are safe to keep references to caches from this script context. if (!isShutdown) { - rootObjectInlineCache->GetInlineCache()->RemoveFromInvalidationList(); + if (rootObjectInlineCache->GetInlineCache()->RemoveFromInvalidationList()) + { + unregisteredInlineCacheCount++; + + } AllocatorDelete(InlineCacheAllocator, this->GetScriptContext()->GetInlineCacheAllocator(), rootObjectInlineCache->GetInlineCache()); } return true; // Remove from the map @@ -123,6 +129,10 @@ namespace Js } ); Assert(found); + if (unregisteredInlineCacheCount > 0) + { + this->GetScriptContext()->GetThreadContext()->NotifyInlineCacheBatchUnregistered(unregisteredInlineCacheCount); + } } BOOL diff --git a/lib/Runtime/Library/ScriptFunction.cpp b/lib/Runtime/Library/ScriptFunction.cpp index b76fbbddd4b..b8f4fc3e31c 100644 --- a/lib/Runtime/Library/ScriptFunction.cpp +++ b/lib/Runtime/Library/ScriptFunction.cpp @@ -753,8 +753,9 @@ namespace Js } } - if (!isShutdown && unregisteredInlineCacheCount > 0 && !scriptContext->IsClosed()) + if (unregisteredInlineCacheCount > 0) { + AssertMsg(!isShutdown && !scriptContext->IsClosed(), "Unregistration of inlineCache should only be done if this is not shutdown or scriptContext closing."); scriptContext->GetThreadContext()->NotifyInlineCacheBatchUnregistered(unregisteredInlineCacheCount); } }