Fix allocation of empty array in the frozen heap for collectible types - #100444

Merged
jkotas merged 13 commits into
dotnet:mainfrom
xoofx:fix-frozen-empty-array-alloc-for-collectible-array-type
Apr 2, 2024
Merged

Fix allocation of empty array in the frozen heap for collectible types#100444
jkotas merged 13 commits into
dotnet:mainfrom
xoofx:fix-frozen-empty-array-alloc-for-collectible-array-type

Conversation

@xoofx

Copy link
Copy Markdown
Member

Fixes#100437

An empty array should not be allocated on a frozen heap if its type is coming from a collectible assembly.

Might be difficult to bring a proper test (e.g is there an API to check if an object is instantiated in a frozen heap?). If it is required, guidance appreciated.

This fix should be backported to net8.0 as well.

@ghostghost added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Mar 29, 2024
@dotnet-policy-servicedotnet-policy-serviceBot added the community-contribution Indicates that the PR has been added by a community member label Mar 29, 2024
Comment threadsrc/coreclr/vm/gchelpers.cpp Outdated
@xoofx

Copy link
Copy Markdown
MemberAuthor

My colleague that has been investigating this is also suggesting adding an assert in FrozenObjectHeapManager::TryAllocateObject to check that a type cannot be collectible.

_ASSERT(type != nullptr);
_ASSERT(FOH_COMMIT_SIZE >= MIN_OBJECT_SIZE);

Thoughts?

I can add it as part of this PR.

@jkotas

Copy link
Copy Markdown
Member

adding an assert

Sounds good to me.

We should also add a test that hits it.

@jkotas

Copy link
Copy Markdown
Member

We should also add a test that hits it.

Let me know if you need help with the test.

@xoofx

Copy link
Copy Markdown
MemberAuthor

Let me know if you need help with the test.

Yes, please, a starting place would be helpful! 😅

@JulieLeeMSFTJulieLeeMSFT added this to the 9.0.0 milestone Mar 29, 2024
@cshung

Copy link
Copy Markdown
Contributor

is there an API to check if an object is instantiated in a frozen heap

Yes, IGCHeap::IsInFrozenSegment should do.

virtual bool IsInFrozenSegment(Object *object) PURE_VIRTUAL

@xoofx

Copy link
Copy Markdown
MemberAuthor

Yes, IGCHeap::IsInFrozenSegment should do.

Thanks! I meant from C# as I'm not familiar how I will be able to make tests only from C++.

@jkotas

Copy link
Copy Markdown
Member

is there an API to check if an object is instantiated in a frozen heap

We prefer to test observable behavior like that the program does not crash. You do not need an API that checks if an object is instantiated in a frozen heap for that.

@jkotas

jkotas commented Mar 30, 2024

Copy link
Copy Markdown
Member

Yes, please, a starting place would be helpful!

This will crash with high probability due to this bug:

usingSystem.Runtime.Loader;publicclassProgram{staticvoidMain(){WeakReference[]wrs=newWeakReference[10];for(inti=0;i<wrs.Length;i++){varalc=newMyAssemblyLoadContext();vara=alc.LoadFromAssemblyPath(typeof(Program).Assembly.Location);wrs[i]=(WeakReference)a.GetType("Program").GetMethod("Work").Invoke(null,null);GC.Collect();}foreach(varwrinwrs){Console.WriteLine(wr.Target);}}publicstaticWeakReferenceWork(){returnnewWeakReference(Array.Empty<Program>());}}classMyAssemblyLoadContext:AssemblyLoadContext{publicMyAssemblyLoadContext():base(isCollectible:true){}}

@xoofx

Copy link
Copy Markdown
MemberAuthor

We prefer to test observable behavior like that the program does not crash. You do not need an API that checks if an object is instantiated in a frozen heap for that.
This will crash with high probability due to this bug:

Makes sense! Where should I add such a test? In src\tests\GC\Scenarios but I see also that there is a src\tests\GC\API\Frozen?

@jkotas

jkotas commented Mar 30, 2024

Copy link
Copy Markdown
Member

As I was writing the test, I have realized that there is an opposite problem too: The frozen object allocated in shared generic static constructor may end up leaking:

classMyG<T>{// This will be allocated on frozen heap, but it is going to leak if T is collectiblestaticobjects=newobject();}

I am not sure what's the best way to fix this leak. We can either detect and reject these problematic patterns as canidates for frozen heap allocation, or we need to pass the containing generic type to the MayBeFrozen allocation helper. cc @EgorBo

@jkotas

Copy link
Copy Markdown
Member

Where should I add such a test?

I would add it under src\tests\JIT\Regression\JitBlue. This is a codegen related problem, so the regression test for it belongs under JIT tests.

@xoofx

Copy link
Copy Markdown
MemberAuthor

Added the test with commit 1189eec but I don't know how to run it. I tried .\build.cmd clr+libs+libs.tests -rc checked -lc release -test but doesn't look that it ran it.

@EgorBo

EgorBo commented Mar 30, 2024

Copy link
Copy Markdown
Member

As I was writing the test, I have realized that there is an opposite problem too: The frozen object allocated in shared generic static constructor may end up leaking:

classMyG<T>{// This will be allocated on frozen heap, but it is going to leak if T is collectiblestaticobjects=newobject();}

I am not sure what's the best way to fix this leak. We can either detect and reject these problematic patterns as canidates for frozen heap allocation, or we need to pass the containing generic type to the MayBeFrozen allocation helper. cc @EgorBo

Good point! I propose this patch here:

- if ((fi.fieldFlags & flagsToCheck) == flagsToCheck)+ if (((fi.fieldFlags & flagsToCheck) == flagsToCheck) &&+ ((info.compCompHnd->getClassAttribs(info.compClassHnd) & CORINFO_FLG_SHAREDINST) == 0))

the logic to detect candidates for NonGC allocators is quite trivial here, I have a prototype for a more advanced version, but it's not ready yet.

@EgorBo

Copy link
Copy Markdown
Member

Good point! I propose this patch here:

Ah, it's a bit too conservative. Basically, with this patch we'll never optimize Array.Empty<string>() into a nongc handle. I presume a proper way is to pass type of current class as an argument in frozen allocator.. but that is not a trivial change so probably for now we should take the conservative patch?

@xoofx
xoofxforce-pushed the fix-frozen-empty-array-alloc-for-collectible-array-type branch from 6050688 to 81578a1CompareMarch 30, 2024 17:06
@jkotas

Copy link
Copy Markdown
Member

Ah, it's a bit too conservative. Basically, with this patch we'll never optimize Array.Empty() into a nongc handle. I presume a proper way is to pass type of current class as an argument in frozen allocator.. but that is not a trivial change so probably for now we should take the conservative patch?

Yes, the conservative fix can be backport candidate. The full fix would be hard to backport.

@xoofx Would you like to include it in this PR? (The test for this case can have similar structure - it should use weak handle to verify that the object is gone after the collectible assembly is unloaded.)

Comment threadsrc/tests/JIT/Regression/JitBlue/Runtime_100437/Runtime_100437.cs Outdated
Comment threadsrc/tests/JIT/Regression/JitBlue/Runtime_100437/Runtime_100437.cs Outdated
Comment threadsrc/tests/JIT/Regression/JitBlue/Runtime_100437/Runtime_100437.csproj Outdated
@xoofx
xoofxforce-pushed the fix-frozen-empty-array-alloc-for-collectible-array-type branch from 81578a1 to a9abd1dCompareMarch 30, 2024 17:58
Comment threadsrc/coreclr/vm/gchelpers.cpp Outdated
@jkotas

Copy link
Copy Markdown
Member

I have pushed test update that hits all interesting cases.

Comment threadsrc/tests/issues.targets Outdated
@EgorBo

Copy link
Copy Markdown
Member

I also usually struggle finding the right syntax for issues.props 😐

@jkotas
jkotas merged commit 59d9749 into dotnet:mainApr 2, 2024
@jkotas

Copy link
Copy Markdown
Member

/backport to release/8.0-staging

@github-actions

Copy link
Copy Markdown
Contributor

Started backporting to release/8.0-staging: https://github.com/dotnet/runtime/actions/runs/8517805878

@github-actions

Copy link
Copy Markdown
Contributor

@jkotas backporting to release/8.0-staging failed, the patch most likely resulted in conflicts:

$ git am --3way --ignore-whitespace --keep-non-patch changes.patch
Applying: Fix allocation of empty array in the frozen heap for collectible types (#100437)
Using index info to reconstruct a base tree...
M	src/coreclr/vm/frozenobjectheap.cpp
M	src/coreclr/vm/gchelpers.cpp
Falling back to patching base and 3-way merge...
Auto-merging src/coreclr/vm/gchelpers.cpp
CONFLICT (content): Merge conflict in src/coreclr/vm/gchelpers.cpp
Auto-merging src/coreclr/vm/frozenobjectheap.cpp
error: Failed to merge in the changes.
hint: Use 'git am --show-current-patch=diff' to see the failed patch
Patch failed at 0001 Fix allocation of empty array in the frozen heap for collectible types (#100437)
When you have resolved this problem, run "git am --continue".
If you prefer to skip this patch, run "git am --skip" instead.
To restore the original branch and stop patching, run "git am --abort".
Error: The process '/usr/bin/git' failed with exit code 128

Please backport manually!

@github-actions

Copy link
Copy Markdown
Contributor

@jkotas an error occurred while backporting to release/8.0-staging, please check the run log for details!

Error: git am failed, most likely due to a merge conflict.

jkotas added a commit that referenced this pull request Apr 2, 2024
#100444)
* Fix allocation of empty array in the frozen heap for collectible types (#100437)
* Remove Optimize from csproj
* Add test for generic with static
* Apply suggestions from code review
* Better test
* Disable tests on Mono
---------
Co-authored-by: Jan Kotas <jkotas@microsoft.com>
@jkotas

Copy link
Copy Markdown
Member

I have opened #100510 on re-enabling the optimization in shared generic with proper lifetime checks.

#100509 is the backport to .NET 8.

@xoofx Thank you for your help with getting this bug found and fixed!

@xoofx

xoofx commented Apr 2, 2024

Copy link
Copy Markdown
MemberAuthor

@xoofx Thank you for your help with getting this bug found and fixed!

Most of the hard work investigating the crash from our side came from @alexey-zakharov☺️

We are super glad that a quick fix was found, as we are heavily relying on collectible ALC and that bug haunted several crashes on our CI. Thanks a lot for helping with it!

alexey-zakharov pushed a commit to Unity-Technologies/runtime that referenced this pull request Apr 2, 2024
dotnet#100444)
* Fix allocation of empty array in the frozen heap for collectible types (dotnet#100437)
* Remove Optimize from csproj
* Add test for generic with static
* Apply suggestions from code review
* Better test
* Disable tests on Mono
---------
Co-authored-by: Jan Kotas <jkotas@microsoft.com>
(cherry picked from commit 78f7707)
jkotas added a commit that referenced this pull request Apr 4, 2024
#100444) (#100509)
* Fix allocation of empty array in the frozen heap for collectible types (#100437)
* Remove Optimize from csproj
* Add test for generic with static
* Apply suggestions from code review
* Better test
* Disable tests on Mono
---------
Co-authored-by: Alexandre Mutel <alexandre_mutel@live.com>
@xoofx
xoofx deleted the fix-frozen-empty-array-alloc-for-collectible-array-type branch April 11, 2024 09:52
matouskozak pushed a commit to matouskozak/runtime that referenced this pull request Apr 30, 2024
dotnet#100444)
* Fix allocation of empty array in the frozen heap for collectible types (dotnet#100437)
* Remove Optimize from csproj
* Add test for generic with static
* Apply suggestions from code review
* Better test
* Disable tests on Mono
---------
Co-authored-by: Jan Kotas <jkotas@microsoft.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators May 12, 2024
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMIcommunity-contributionIndicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Empty array allocated on the Frozen Heap for a Collectible type?

6 participants

@xoofx@jkotas@cshung@EgorBo@MichalPetryka@JulieLeeMSFT
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Fix allocation of empty array in the frozen heap for collectible types - #100444

Merged
jkotas merged 13 commits into
dotnet:mainfrom
xoofx:fix-frozen-empty-array-alloc-for-collectible-array-type
Apr 2, 2024
Merged

Fix allocation of empty array in the frozen heap for collectible types#100444
jkotas merged 13 commits into
dotnet:mainfrom
xoofx:fix-frozen-empty-array-alloc-for-collectible-array-type

Conversation

@xoofx

Copy link
Copy Markdown
Member

Fixes#100437

An empty array should not be allocated on a frozen heap if its type is coming from a collectible assembly.

Might be difficult to bring a proper test (e.g is there an API to check if an object is instantiated in a frozen heap?). If it is required, guidance appreciated.

This fix should be backported to net8.0 as well.

@ghostghost added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Mar 29, 2024
@dotnet-policy-servicedotnet-policy-serviceBot added the community-contribution Indicates that the PR has been added by a community member label Mar 29, 2024
Comment threadsrc/coreclr/vm/gchelpers.cpp Outdated
@xoofx

Copy link
Copy Markdown
MemberAuthor

My colleague that has been investigating this is also suggesting adding an assert in FrozenObjectHeapManager::TryAllocateObject to check that a type cannot be collectible.

_ASSERT(type != nullptr);
_ASSERT(FOH_COMMIT_SIZE >= MIN_OBJECT_SIZE);

Thoughts?

I can add it as part of this PR.

@jkotas

Copy link
Copy Markdown
Member

adding an assert

Sounds good to me.

We should also add a test that hits it.

@jkotas

Copy link
Copy Markdown
Member

We should also add a test that hits it.

Let me know if you need help with the test.

@xoofx

Copy link
Copy Markdown
MemberAuthor

Let me know if you need help with the test.

Yes, please, a starting place would be helpful! 😅

@JulieLeeMSFTJulieLeeMSFT added this to the 9.0.0 milestone Mar 29, 2024
@cshung

Copy link
Copy Markdown
Contributor

is there an API to check if an object is instantiated in a frozen heap

Yes, IGCHeap::IsInFrozenSegment should do.

virtual bool IsInFrozenSegment(Object *object) PURE_VIRTUAL

@xoofx

Copy link
Copy Markdown
MemberAuthor

Yes, IGCHeap::IsInFrozenSegment should do.

Thanks! I meant from C# as I'm not familiar how I will be able to make tests only from C++.

@jkotas

Copy link
Copy Markdown
Member

is there an API to check if an object is instantiated in a frozen heap

We prefer to test observable behavior like that the program does not crash. You do not need an API that checks if an object is instantiated in a frozen heap for that.

@jkotas

jkotas commented Mar 30, 2024

Copy link
Copy Markdown
Member

Yes, please, a starting place would be helpful!

This will crash with high probability due to this bug:

usingSystem.Runtime.Loader;publicclassProgram{staticvoidMain(){WeakReference[]wrs=newWeakReference[10];for(inti=0;i<wrs.Length;i++){varalc=newMyAssemblyLoadContext();vara=alc.LoadFromAssemblyPath(typeof(Program).Assembly.Location);wrs[i]=(WeakReference)a.GetType("Program").GetMethod("Work").Invoke(null,null);GC.Collect();}foreach(varwrinwrs){Console.WriteLine(wr.Target);}}publicstaticWeakReferenceWork(){returnnewWeakReference(Array.Empty<Program>());}}classMyAssemblyLoadContext:AssemblyLoadContext{publicMyAssemblyLoadContext():base(isCollectible:true){}}

@xoofx

Copy link
Copy Markdown
MemberAuthor

We prefer to test observable behavior like that the program does not crash. You do not need an API that checks if an object is instantiated in a frozen heap for that.
This will crash with high probability due to this bug:

Makes sense! Where should I add such a test? In src\tests\GC\Scenarios but I see also that there is a src\tests\GC\API\Frozen?

@jkotas

jkotas commented Mar 30, 2024

Copy link
Copy Markdown
Member

As I was writing the test, I have realized that there is an opposite problem too: The frozen object allocated in shared generic static constructor may end up leaking:

classMyG<T>{// This will be allocated on frozen heap, but it is going to leak if T is collectiblestaticobjects=newobject();}

I am not sure what's the best way to fix this leak. We can either detect and reject these problematic patterns as canidates for frozen heap allocation, or we need to pass the containing generic type to the MayBeFrozen allocation helper. cc @EgorBo

@jkotas

Copy link
Copy Markdown
Member

Where should I add such a test?

I would add it under src\tests\JIT\Regression\JitBlue. This is a codegen related problem, so the regression test for it belongs under JIT tests.

@xoofx

Copy link
Copy Markdown
MemberAuthor

Added the test with commit 1189eec but I don't know how to run it. I tried .\build.cmd clr+libs+libs.tests -rc checked -lc release -test but doesn't look that it ran it.

@EgorBo

EgorBo commented Mar 30, 2024

Copy link
Copy Markdown
Member

As I was writing the test, I have realized that there is an opposite problem too: The frozen object allocated in shared generic static constructor may end up leaking:

classMyG<T>{// This will be allocated on frozen heap, but it is going to leak if T is collectiblestaticobjects=newobject();}

I am not sure what's the best way to fix this leak. We can either detect and reject these problematic patterns as canidates for frozen heap allocation, or we need to pass the containing generic type to the MayBeFrozen allocation helper. cc @EgorBo

Good point! I propose this patch here:

- if ((fi.fieldFlags & flagsToCheck) == flagsToCheck)+ if (((fi.fieldFlags & flagsToCheck) == flagsToCheck) &&+ ((info.compCompHnd->getClassAttribs(info.compClassHnd) & CORINFO_FLG_SHAREDINST) == 0))

the logic to detect candidates for NonGC allocators is quite trivial here, I have a prototype for a more advanced version, but it's not ready yet.

@EgorBo

Copy link
Copy Markdown
Member

Good point! I propose this patch here:

Ah, it's a bit too conservative. Basically, with this patch we'll never optimize Array.Empty<string>() into a nongc handle. I presume a proper way is to pass type of current class as an argument in frozen allocator.. but that is not a trivial change so probably for now we should take the conservative patch?

@xoofx
xoofxforce-pushed the fix-frozen-empty-array-alloc-for-collectible-array-type branch from 6050688 to 81578a1CompareMarch 30, 2024 17:06
@jkotas

Copy link
Copy Markdown
Member

Ah, it's a bit too conservative. Basically, with this patch we'll never optimize Array.Empty() into a nongc handle. I presume a proper way is to pass type of current class as an argument in frozen allocator.. but that is not a trivial change so probably for now we should take the conservative patch?

Yes, the conservative fix can be backport candidate. The full fix would be hard to backport.

@xoofx Would you like to include it in this PR? (The test for this case can have similar structure - it should use weak handle to verify that the object is gone after the collectible assembly is unloaded.)

Comment threadsrc/tests/JIT/Regression/JitBlue/Runtime_100437/Runtime_100437.cs Outdated
Comment threadsrc/tests/JIT/Regression/JitBlue/Runtime_100437/Runtime_100437.cs Outdated
Comment threadsrc/tests/JIT/Regression/JitBlue/Runtime_100437/Runtime_100437.csproj Outdated
@xoofx
xoofxforce-pushed the fix-frozen-empty-array-alloc-for-collectible-array-type branch from 81578a1 to a9abd1dCompareMarch 30, 2024 17:58
Comment threadsrc/coreclr/vm/gchelpers.cpp Outdated
@jkotas

Copy link
Copy Markdown
Member

I have pushed test update that hits all interesting cases.

Comment threadsrc/tests/issues.targets Outdated
@EgorBo

Copy link
Copy Markdown
Member

I also usually struggle finding the right syntax for issues.props 😐

@jkotas
jkotas merged commit 59d9749 into dotnet:mainApr 2, 2024
@jkotas

Copy link
Copy Markdown
Member

/backport to release/8.0-staging

@github-actions

Copy link
Copy Markdown
Contributor

Started backporting to release/8.0-staging: https://github.com/dotnet/runtime/actions/runs/8517805878

@github-actions

Copy link
Copy Markdown
Contributor

@jkotas backporting to release/8.0-staging failed, the patch most likely resulted in conflicts:

$ git am --3way --ignore-whitespace --keep-non-patch changes.patch
Applying: Fix allocation of empty array in the frozen heap for collectible types (#100437)
Using index info to reconstruct a base tree...
M	src/coreclr/vm/frozenobjectheap.cpp
M	src/coreclr/vm/gchelpers.cpp
Falling back to patching base and 3-way merge...
Auto-merging src/coreclr/vm/gchelpers.cpp
CONFLICT (content): Merge conflict in src/coreclr/vm/gchelpers.cpp
Auto-merging src/coreclr/vm/frozenobjectheap.cpp
error: Failed to merge in the changes.
hint: Use 'git am --show-current-patch=diff' to see the failed patch
Patch failed at 0001 Fix allocation of empty array in the frozen heap for collectible types (#100437)
When you have resolved this problem, run "git am --continue".
If you prefer to skip this patch, run "git am --skip" instead.
To restore the original branch and stop patching, run "git am --abort".
Error: The process '/usr/bin/git' failed with exit code 128

Please backport manually!

@github-actions

Copy link
Copy Markdown
Contributor

@jkotas an error occurred while backporting to release/8.0-staging, please check the run log for details!

Error: git am failed, most likely due to a merge conflict.

jkotas added a commit that referenced this pull request Apr 2, 2024
#100444)
* Fix allocation of empty array in the frozen heap for collectible types (#100437)
* Remove Optimize from csproj
* Add test for generic with static
* Apply suggestions from code review
* Better test
* Disable tests on Mono
---------
Co-authored-by: Jan Kotas <jkotas@microsoft.com>
@jkotas

Copy link
Copy Markdown
Member

I have opened #100510 on re-enabling the optimization in shared generic with proper lifetime checks.

#100509 is the backport to .NET 8.

@xoofx Thank you for your help with getting this bug found and fixed!

@xoofx

xoofx commented Apr 2, 2024

Copy link
Copy Markdown
MemberAuthor

@xoofx Thank you for your help with getting this bug found and fixed!

Most of the hard work investigating the crash from our side came from @alexey-zakharov☺️

We are super glad that a quick fix was found, as we are heavily relying on collectible ALC and that bug haunted several crashes on our CI. Thanks a lot for helping with it!

alexey-zakharov pushed a commit to Unity-Technologies/runtime that referenced this pull request Apr 2, 2024
dotnet#100444)
* Fix allocation of empty array in the frozen heap for collectible types (dotnet#100437)
* Remove Optimize from csproj
* Add test for generic with static
* Apply suggestions from code review
* Better test
* Disable tests on Mono
---------
Co-authored-by: Jan Kotas <jkotas@microsoft.com>
(cherry picked from commit 78f7707)
jkotas added a commit that referenced this pull request Apr 4, 2024
#100444) (#100509)
* Fix allocation of empty array in the frozen heap for collectible types (#100437)
* Remove Optimize from csproj
* Add test for generic with static
* Apply suggestions from code review
* Better test
* Disable tests on Mono
---------
Co-authored-by: Alexandre Mutel <alexandre_mutel@live.com>
@xoofx
xoofx deleted the fix-frozen-empty-array-alloc-for-collectible-array-type branch April 11, 2024 09:52
matouskozak pushed a commit to matouskozak/runtime that referenced this pull request Apr 30, 2024
dotnet#100444)
* Fix allocation of empty array in the frozen heap for collectible types (dotnet#100437)
* Remove Optimize from csproj
* Add test for generic with static
* Apply suggestions from code review
* Better test
* Disable tests on Mono
---------
Co-authored-by: Jan Kotas <jkotas@microsoft.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators May 12, 2024
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMIcommunity-contributionIndicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Empty array allocated on the Frozen Heap for a Collectible type?

6 participants

@xoofx@jkotas@cshung@EgorBo@MichalPetryka@JulieLeeMSFT
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Fix allocation of empty array in the frozen heap for collectible types - #100444

Merged
jkotas merged 13 commits into
dotnet:mainfrom
xoofx:fix-frozen-empty-array-alloc-for-collectible-array-type
Apr 2, 2024
Merged

Fix allocation of empty array in the frozen heap for collectible types#100444
jkotas merged 13 commits into
dotnet:mainfrom
xoofx:fix-frozen-empty-array-alloc-for-collectible-array-type

Conversation

@xoofx

Copy link
Copy Markdown
Member

Fixes#100437

An empty array should not be allocated on a frozen heap if its type is coming from a collectible assembly.

Might be difficult to bring a proper test (e.g is there an API to check if an object is instantiated in a frozen heap?). If it is required, guidance appreciated.

This fix should be backported to net8.0 as well.

@ghostghost added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Mar 29, 2024
@dotnet-policy-servicedotnet-policy-serviceBot added the community-contribution Indicates that the PR has been added by a community member label Mar 29, 2024
Comment threadsrc/coreclr/vm/gchelpers.cpp Outdated
@xoofx

Copy link
Copy Markdown
MemberAuthor

My colleague that has been investigating this is also suggesting adding an assert in FrozenObjectHeapManager::TryAllocateObject to check that a type cannot be collectible.

_ASSERT(type != nullptr);
_ASSERT(FOH_COMMIT_SIZE >= MIN_OBJECT_SIZE);

Thoughts?

I can add it as part of this PR.

@jkotas

Copy link
Copy Markdown
Member

adding an assert

Sounds good to me.

We should also add a test that hits it.

@jkotas

Copy link
Copy Markdown
Member

We should also add a test that hits it.

Let me know if you need help with the test.

@xoofx

Copy link
Copy Markdown
MemberAuthor

Let me know if you need help with the test.

Yes, please, a starting place would be helpful! 😅

@JulieLeeMSFTJulieLeeMSFT added this to the 9.0.0 milestone Mar 29, 2024
@cshung

Copy link
Copy Markdown
Contributor

is there an API to check if an object is instantiated in a frozen heap

Yes, IGCHeap::IsInFrozenSegment should do.

virtual bool IsInFrozenSegment(Object *object) PURE_VIRTUAL

@xoofx

Copy link
Copy Markdown
MemberAuthor

Yes, IGCHeap::IsInFrozenSegment should do.

Thanks! I meant from C# as I'm not familiar how I will be able to make tests only from C++.

@jkotas

Copy link
Copy Markdown
Member

is there an API to check if an object is instantiated in a frozen heap

We prefer to test observable behavior like that the program does not crash. You do not need an API that checks if an object is instantiated in a frozen heap for that.

@jkotas

jkotas commented Mar 30, 2024

Copy link
Copy Markdown
Member

Yes, please, a starting place would be helpful!

This will crash with high probability due to this bug:

usingSystem.Runtime.Loader;publicclassProgram{staticvoidMain(){WeakReference[]wrs=newWeakReference[10];for(inti=0;i<wrs.Length;i++){varalc=newMyAssemblyLoadContext();vara=alc.LoadFromAssemblyPath(typeof(Program).Assembly.Location);wrs[i]=(WeakReference)a.GetType("Program").GetMethod("Work").Invoke(null,null);GC.Collect();}foreach(varwrinwrs){Console.WriteLine(wr.Target);}}publicstaticWeakReferenceWork(){returnnewWeakReference(Array.Empty<Program>());}}classMyAssemblyLoadContext:AssemblyLoadContext{publicMyAssemblyLoadContext():base(isCollectible:true){}}

@xoofx

Copy link
Copy Markdown
MemberAuthor

We prefer to test observable behavior like that the program does not crash. You do not need an API that checks if an object is instantiated in a frozen heap for that.
This will crash with high probability due to this bug:

Makes sense! Where should I add such a test? In src\tests\GC\Scenarios but I see also that there is a src\tests\GC\API\Frozen?

@jkotas

jkotas commented Mar 30, 2024

Copy link
Copy Markdown
Member

As I was writing the test, I have realized that there is an opposite problem too: The frozen object allocated in shared generic static constructor may end up leaking:

classMyG<T>{// This will be allocated on frozen heap, but it is going to leak if T is collectiblestaticobjects=newobject();}

I am not sure what's the best way to fix this leak. We can either detect and reject these problematic patterns as canidates for frozen heap allocation, or we need to pass the containing generic type to the MayBeFrozen allocation helper. cc @EgorBo

@jkotas

Copy link
Copy Markdown
Member

Where should I add such a test?

I would add it under src\tests\JIT\Regression\JitBlue. This is a codegen related problem, so the regression test for it belongs under JIT tests.

@xoofx

Copy link
Copy Markdown
MemberAuthor

Added the test with commit 1189eec but I don't know how to run it. I tried .\build.cmd clr+libs+libs.tests -rc checked -lc release -test but doesn't look that it ran it.

@EgorBo

EgorBo commented Mar 30, 2024

Copy link
Copy Markdown
Member

As I was writing the test, I have realized that there is an opposite problem too: The frozen object allocated in shared generic static constructor may end up leaking:

classMyG<T>{// This will be allocated on frozen heap, but it is going to leak if T is collectiblestaticobjects=newobject();}

I am not sure what's the best way to fix this leak. We can either detect and reject these problematic patterns as canidates for frozen heap allocation, or we need to pass the containing generic type to the MayBeFrozen allocation helper. cc @EgorBo

Good point! I propose this patch here:

- if ((fi.fieldFlags & flagsToCheck) == flagsToCheck)+ if (((fi.fieldFlags & flagsToCheck) == flagsToCheck) &&+ ((info.compCompHnd->getClassAttribs(info.compClassHnd) & CORINFO_FLG_SHAREDINST) == 0))

the logic to detect candidates for NonGC allocators is quite trivial here, I have a prototype for a more advanced version, but it's not ready yet.

@EgorBo

Copy link
Copy Markdown
Member

Good point! I propose this patch here:

Ah, it's a bit too conservative. Basically, with this patch we'll never optimize Array.Empty<string>() into a nongc handle. I presume a proper way is to pass type of current class as an argument in frozen allocator.. but that is not a trivial change so probably for now we should take the conservative patch?

@xoofx
xoofxforce-pushed the fix-frozen-empty-array-alloc-for-collectible-array-type branch from 6050688 to 81578a1CompareMarch 30, 2024 17:06
@jkotas

Copy link
Copy Markdown
Member

Ah, it's a bit too conservative. Basically, with this patch we'll never optimize Array.Empty() into a nongc handle. I presume a proper way is to pass type of current class as an argument in frozen allocator.. but that is not a trivial change so probably for now we should take the conservative patch?

Yes, the conservative fix can be backport candidate. The full fix would be hard to backport.

@xoofx Would you like to include it in this PR? (The test for this case can have similar structure - it should use weak handle to verify that the object is gone after the collectible assembly is unloaded.)

Comment threadsrc/tests/JIT/Regression/JitBlue/Runtime_100437/Runtime_100437.cs Outdated
Comment threadsrc/tests/JIT/Regression/JitBlue/Runtime_100437/Runtime_100437.cs Outdated
Comment threadsrc/tests/JIT/Regression/JitBlue/Runtime_100437/Runtime_100437.csproj Outdated
@xoofx
xoofxforce-pushed the fix-frozen-empty-array-alloc-for-collectible-array-type branch from 81578a1 to a9abd1dCompareMarch 30, 2024 17:58
Comment threadsrc/coreclr/vm/gchelpers.cpp Outdated
@jkotas

Copy link
Copy Markdown
Member

I have pushed test update that hits all interesting cases.

Comment threadsrc/tests/issues.targets Outdated
@EgorBo

Copy link
Copy Markdown
Member

I also usually struggle finding the right syntax for issues.props 😐

@jkotas
jkotas merged commit 59d9749 into dotnet:mainApr 2, 2024
@jkotas

Copy link
Copy Markdown
Member

/backport to release/8.0-staging

@github-actions

Copy link
Copy Markdown
Contributor

Started backporting to release/8.0-staging: https://github.com/dotnet/runtime/actions/runs/8517805878

@github-actions

Copy link
Copy Markdown
Contributor

@jkotas backporting to release/8.0-staging failed, the patch most likely resulted in conflicts:

$ git am --3way --ignore-whitespace --keep-non-patch changes.patch
Applying: Fix allocation of empty array in the frozen heap for collectible types (#100437)
Using index info to reconstruct a base tree...
M	src/coreclr/vm/frozenobjectheap.cpp
M	src/coreclr/vm/gchelpers.cpp
Falling back to patching base and 3-way merge...
Auto-merging src/coreclr/vm/gchelpers.cpp
CONFLICT (content): Merge conflict in src/coreclr/vm/gchelpers.cpp
Auto-merging src/coreclr/vm/frozenobjectheap.cpp
error: Failed to merge in the changes.
hint: Use 'git am --show-current-patch=diff' to see the failed patch
Patch failed at 0001 Fix allocation of empty array in the frozen heap for collectible types (#100437)
When you have resolved this problem, run "git am --continue".
If you prefer to skip this patch, run "git am --skip" instead.
To restore the original branch and stop patching, run "git am --abort".
Error: The process '/usr/bin/git' failed with exit code 128

Please backport manually!

@github-actions

Copy link
Copy Markdown
Contributor

@jkotas an error occurred while backporting to release/8.0-staging, please check the run log for details!

Error: git am failed, most likely due to a merge conflict.

jkotas added a commit that referenced this pull request Apr 2, 2024
#100444)
* Fix allocation of empty array in the frozen heap for collectible types (#100437)
* Remove Optimize from csproj
* Add test for generic with static
* Apply suggestions from code review
* Better test
* Disable tests on Mono
---------
Co-authored-by: Jan Kotas <jkotas@microsoft.com>
@jkotas

Copy link
Copy Markdown
Member

I have opened #100510 on re-enabling the optimization in shared generic with proper lifetime checks.

#100509 is the backport to .NET 8.

@xoofx Thank you for your help with getting this bug found and fixed!

@xoofx

xoofx commented Apr 2, 2024

Copy link
Copy Markdown
MemberAuthor

@xoofx Thank you for your help with getting this bug found and fixed!

Most of the hard work investigating the crash from our side came from @alexey-zakharov☺️

We are super glad that a quick fix was found, as we are heavily relying on collectible ALC and that bug haunted several crashes on our CI. Thanks a lot for helping with it!

alexey-zakharov pushed a commit to Unity-Technologies/runtime that referenced this pull request Apr 2, 2024
dotnet#100444)
* Fix allocation of empty array in the frozen heap for collectible types (dotnet#100437)
* Remove Optimize from csproj
* Add test for generic with static
* Apply suggestions from code review
* Better test
* Disable tests on Mono
---------
Co-authored-by: Jan Kotas <jkotas@microsoft.com>
(cherry picked from commit 78f7707)
jkotas added a commit that referenced this pull request Apr 4, 2024
#100444) (#100509)
* Fix allocation of empty array in the frozen heap for collectible types (#100437)
* Remove Optimize from csproj
* Add test for generic with static
* Apply suggestions from code review
* Better test
* Disable tests on Mono
---------
Co-authored-by: Alexandre Mutel <alexandre_mutel@live.com>
@xoofx
xoofx deleted the fix-frozen-empty-array-alloc-for-collectible-array-type branch April 11, 2024 09:52
matouskozak pushed a commit to matouskozak/runtime that referenced this pull request Apr 30, 2024
dotnet#100444)
* Fix allocation of empty array in the frozen heap for collectible types (dotnet#100437)
* Remove Optimize from csproj
* Add test for generic with static
* Apply suggestions from code review
* Better test
* Disable tests on Mono
---------
Co-authored-by: Jan Kotas <jkotas@microsoft.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators May 12, 2024
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMIcommunity-contributionIndicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Empty array allocated on the Frozen Heap for a Collectible type?

6 participants

@xoofx@jkotas@cshung@EgorBo@MichalPetryka@JulieLeeMSFT
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Fix allocation of empty array in the frozen heap for collectible types - #100444

Merged
jkotas merged 13 commits into
dotnet:mainfrom
xoofx:fix-frozen-empty-array-alloc-for-collectible-array-type
Apr 2, 2024
Merged

Fix allocation of empty array in the frozen heap for collectible types#100444
jkotas merged 13 commits into
dotnet:mainfrom
xoofx:fix-frozen-empty-array-alloc-for-collectible-array-type

Conversation

@xoofx

Copy link
Copy Markdown
Member

Fixes#100437

An empty array should not be allocated on a frozen heap if its type is coming from a collectible assembly.

Might be difficult to bring a proper test (e.g is there an API to check if an object is instantiated in a frozen heap?). If it is required, guidance appreciated.

This fix should be backported to net8.0 as well.

@ghostghost added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Mar 29, 2024
@dotnet-policy-servicedotnet-policy-serviceBot added the community-contribution Indicates that the PR has been added by a community member label Mar 29, 2024
Comment threadsrc/coreclr/vm/gchelpers.cpp Outdated
@xoofx

Copy link
Copy Markdown
MemberAuthor

My colleague that has been investigating this is also suggesting adding an assert in FrozenObjectHeapManager::TryAllocateObject to check that a type cannot be collectible.

_ASSERT(type != nullptr);
_ASSERT(FOH_COMMIT_SIZE >= MIN_OBJECT_SIZE);

Thoughts?

I can add it as part of this PR.

@jkotas

Copy link
Copy Markdown
Member

adding an assert

Sounds good to me.

We should also add a test that hits it.

@jkotas

Copy link
Copy Markdown
Member

We should also add a test that hits it.

Let me know if you need help with the test.

@xoofx

Copy link
Copy Markdown
MemberAuthor

Let me know if you need help with the test.

Yes, please, a starting place would be helpful! 😅

@JulieLeeMSFTJulieLeeMSFT added this to the 9.0.0 milestone Mar 29, 2024
@cshung

Copy link
Copy Markdown
Contributor

is there an API to check if an object is instantiated in a frozen heap

Yes, IGCHeap::IsInFrozenSegment should do.

virtual bool IsInFrozenSegment(Object *object) PURE_VIRTUAL

@xoofx

Copy link
Copy Markdown
MemberAuthor

Yes, IGCHeap::IsInFrozenSegment should do.

Thanks! I meant from C# as I'm not familiar how I will be able to make tests only from C++.

@jkotas

Copy link
Copy Markdown
Member

is there an API to check if an object is instantiated in a frozen heap

We prefer to test observable behavior like that the program does not crash. You do not need an API that checks if an object is instantiated in a frozen heap for that.

@jkotas

jkotas commented Mar 30, 2024

Copy link
Copy Markdown
Member

Yes, please, a starting place would be helpful!

This will crash with high probability due to this bug:

usingSystem.Runtime.Loader;publicclassProgram{staticvoidMain(){WeakReference[]wrs=newWeakReference[10];for(inti=0;i<wrs.Length;i++){varalc=newMyAssemblyLoadContext();vara=alc.LoadFromAssemblyPath(typeof(Program).Assembly.Location);wrs[i]=(WeakReference)a.GetType("Program").GetMethod("Work").Invoke(null,null);GC.Collect();}foreach(varwrinwrs){Console.WriteLine(wr.Target);}}publicstaticWeakReferenceWork(){returnnewWeakReference(Array.Empty<Program>());}}classMyAssemblyLoadContext:AssemblyLoadContext{publicMyAssemblyLoadContext():base(isCollectible:true){}}

@xoofx

Copy link
Copy Markdown
MemberAuthor

We prefer to test observable behavior like that the program does not crash. You do not need an API that checks if an object is instantiated in a frozen heap for that.
This will crash with high probability due to this bug:

Makes sense! Where should I add such a test? In src\tests\GC\Scenarios but I see also that there is a src\tests\GC\API\Frozen?

@jkotas

jkotas commented Mar 30, 2024

Copy link
Copy Markdown
Member

As I was writing the test, I have realized that there is an opposite problem too: The frozen object allocated in shared generic static constructor may end up leaking:

classMyG<T>{// This will be allocated on frozen heap, but it is going to leak if T is collectiblestaticobjects=newobject();}

I am not sure what's the best way to fix this leak. We can either detect and reject these problematic patterns as canidates for frozen heap allocation, or we need to pass the containing generic type to the MayBeFrozen allocation helper. cc @EgorBo

@jkotas

Copy link
Copy Markdown
Member

Where should I add such a test?

I would add it under src\tests\JIT\Regression\JitBlue. This is a codegen related problem, so the regression test for it belongs under JIT tests.

@xoofx

Copy link
Copy Markdown
MemberAuthor

Added the test with commit 1189eec but I don't know how to run it. I tried .\build.cmd clr+libs+libs.tests -rc checked -lc release -test but doesn't look that it ran it.

@EgorBo

EgorBo commented Mar 30, 2024

Copy link
Copy Markdown
Member

As I was writing the test, I have realized that there is an opposite problem too: The frozen object allocated in shared generic static constructor may end up leaking:

classMyG<T>{// This will be allocated on frozen heap, but it is going to leak if T is collectiblestaticobjects=newobject();}

I am not sure what's the best way to fix this leak. We can either detect and reject these problematic patterns as canidates for frozen heap allocation, or we need to pass the containing generic type to the MayBeFrozen allocation helper. cc @EgorBo

Good point! I propose this patch here:

- if ((fi.fieldFlags & flagsToCheck) == flagsToCheck)+ if (((fi.fieldFlags & flagsToCheck) == flagsToCheck) &&+ ((info.compCompHnd->getClassAttribs(info.compClassHnd) & CORINFO_FLG_SHAREDINST) == 0))

the logic to detect candidates for NonGC allocators is quite trivial here, I have a prototype for a more advanced version, but it's not ready yet.

@EgorBo

Copy link
Copy Markdown
Member

Good point! I propose this patch here:

Ah, it's a bit too conservative. Basically, with this patch we'll never optimize Array.Empty<string>() into a nongc handle. I presume a proper way is to pass type of current class as an argument in frozen allocator.. but that is not a trivial change so probably for now we should take the conservative patch?

@xoofx
xoofxforce-pushed the fix-frozen-empty-array-alloc-for-collectible-array-type branch from 6050688 to 81578a1CompareMarch 30, 2024 17:06
@jkotas

Copy link
Copy Markdown
Member

Ah, it's a bit too conservative. Basically, with this patch we'll never optimize Array.Empty() into a nongc handle. I presume a proper way is to pass type of current class as an argument in frozen allocator.. but that is not a trivial change so probably for now we should take the conservative patch?

Yes, the conservative fix can be backport candidate. The full fix would be hard to backport.

@xoofx Would you like to include it in this PR? (The test for this case can have similar structure - it should use weak handle to verify that the object is gone after the collectible assembly is unloaded.)

Comment threadsrc/tests/JIT/Regression/JitBlue/Runtime_100437/Runtime_100437.cs Outdated
Comment threadsrc/tests/JIT/Regression/JitBlue/Runtime_100437/Runtime_100437.cs Outdated
Comment threadsrc/tests/JIT/Regression/JitBlue/Runtime_100437/Runtime_100437.csproj Outdated
@xoofx
xoofxforce-pushed the fix-frozen-empty-array-alloc-for-collectible-array-type branch from 81578a1 to a9abd1dCompareMarch 30, 2024 17:58
Comment threadsrc/coreclr/vm/gchelpers.cpp Outdated
@jkotas

Copy link
Copy Markdown
Member

I have pushed test update that hits all interesting cases.

Comment threadsrc/tests/issues.targets Outdated
@EgorBo

Copy link
Copy Markdown
Member

I also usually struggle finding the right syntax for issues.props 😐

@jkotas
jkotas merged commit 59d9749 into dotnet:mainApr 2, 2024
@jkotas

Copy link
Copy Markdown
Member

/backport to release/8.0-staging

@github-actions

Copy link
Copy Markdown
Contributor

Started backporting to release/8.0-staging: https://github.com/dotnet/runtime/actions/runs/8517805878

@github-actions

Copy link
Copy Markdown
Contributor

@jkotas backporting to release/8.0-staging failed, the patch most likely resulted in conflicts:

$ git am --3way --ignore-whitespace --keep-non-patch changes.patch
Applying: Fix allocation of empty array in the frozen heap for collectible types (#100437)
Using index info to reconstruct a base tree...
M	src/coreclr/vm/frozenobjectheap.cpp
M	src/coreclr/vm/gchelpers.cpp
Falling back to patching base and 3-way merge...
Auto-merging src/coreclr/vm/gchelpers.cpp
CONFLICT (content): Merge conflict in src/coreclr/vm/gchelpers.cpp
Auto-merging src/coreclr/vm/frozenobjectheap.cpp
error: Failed to merge in the changes.
hint: Use 'git am --show-current-patch=diff' to see the failed patch
Patch failed at 0001 Fix allocation of empty array in the frozen heap for collectible types (#100437)
When you have resolved this problem, run "git am --continue".
If you prefer to skip this patch, run "git am --skip" instead.
To restore the original branch and stop patching, run "git am --abort".
Error: The process '/usr/bin/git' failed with exit code 128

Please backport manually!

@github-actions

Copy link
Copy Markdown
Contributor

@jkotas an error occurred while backporting to release/8.0-staging, please check the run log for details!

Error: git am failed, most likely due to a merge conflict.

jkotas added a commit that referenced this pull request Apr 2, 2024
#100444)
* Fix allocation of empty array in the frozen heap for collectible types (#100437)
* Remove Optimize from csproj
* Add test for generic with static
* Apply suggestions from code review
* Better test
* Disable tests on Mono
---------
Co-authored-by: Jan Kotas <jkotas@microsoft.com>
@jkotas

Copy link
Copy Markdown
Member

I have opened #100510 on re-enabling the optimization in shared generic with proper lifetime checks.

#100509 is the backport to .NET 8.

@xoofx Thank you for your help with getting this bug found and fixed!

@xoofx

xoofx commented Apr 2, 2024

Copy link
Copy Markdown
MemberAuthor

@xoofx Thank you for your help with getting this bug found and fixed!

Most of the hard work investigating the crash from our side came from @alexey-zakharov☺️

We are super glad that a quick fix was found, as we are heavily relying on collectible ALC and that bug haunted several crashes on our CI. Thanks a lot for helping with it!

alexey-zakharov pushed a commit to Unity-Technologies/runtime that referenced this pull request Apr 2, 2024
dotnet#100444)
* Fix allocation of empty array in the frozen heap for collectible types (dotnet#100437)
* Remove Optimize from csproj
* Add test for generic with static
* Apply suggestions from code review
* Better test
* Disable tests on Mono
---------
Co-authored-by: Jan Kotas <jkotas@microsoft.com>
(cherry picked from commit 78f7707)
jkotas added a commit that referenced this pull request Apr 4, 2024
#100444) (#100509)
* Fix allocation of empty array in the frozen heap for collectible types (#100437)
* Remove Optimize from csproj
* Add test for generic with static
* Apply suggestions from code review
* Better test
* Disable tests on Mono
---------
Co-authored-by: Alexandre Mutel <alexandre_mutel@live.com>
@xoofx
xoofx deleted the fix-frozen-empty-array-alloc-for-collectible-array-type branch April 11, 2024 09:52
matouskozak pushed a commit to matouskozak/runtime that referenced this pull request Apr 30, 2024
dotnet#100444)
* Fix allocation of empty array in the frozen heap for collectible types (dotnet#100437)
* Remove Optimize from csproj
* Add test for generic with static
* Apply suggestions from code review
* Better test
* Disable tests on Mono
---------
Co-authored-by: Jan Kotas <jkotas@microsoft.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators May 12, 2024
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMIcommunity-contributionIndicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Empty array allocated on the Frozen Heap for a Collectible type?

6 participants

@xoofx@jkotas@cshung@EgorBo@MichalPetryka@JulieLeeMSFT
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Fix allocation of empty array in the frozen heap for collectible types - #100444

Merged
jkotas merged 13 commits into
dotnet:mainfrom
xoofx:fix-frozen-empty-array-alloc-for-collectible-array-type
Apr 2, 2024
Merged

Fix allocation of empty array in the frozen heap for collectible types#100444
jkotas merged 13 commits into
dotnet:mainfrom
xoofx:fix-frozen-empty-array-alloc-for-collectible-array-type

Conversation

@xoofx

Copy link
Copy Markdown
Member

Fixes#100437

An empty array should not be allocated on a frozen heap if its type is coming from a collectible assembly.

Might be difficult to bring a proper test (e.g is there an API to check if an object is instantiated in a frozen heap?). If it is required, guidance appreciated.

This fix should be backported to net8.0 as well.

@ghostghost added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Mar 29, 2024
@dotnet-policy-servicedotnet-policy-serviceBot added the community-contribution Indicates that the PR has been added by a community member label Mar 29, 2024
Comment threadsrc/coreclr/vm/gchelpers.cpp Outdated
@xoofx

Copy link
Copy Markdown
MemberAuthor

My colleague that has been investigating this is also suggesting adding an assert in FrozenObjectHeapManager::TryAllocateObject to check that a type cannot be collectible.

_ASSERT(type != nullptr);
_ASSERT(FOH_COMMIT_SIZE >= MIN_OBJECT_SIZE);

Thoughts?

I can add it as part of this PR.

@jkotas

Copy link
Copy Markdown
Member

adding an assert

Sounds good to me.

We should also add a test that hits it.

@jkotas

Copy link
Copy Markdown
Member

We should also add a test that hits it.

Let me know if you need help with the test.

@xoofx

Copy link
Copy Markdown
MemberAuthor

Let me know if you need help with the test.

Yes, please, a starting place would be helpful! 😅

@JulieLeeMSFTJulieLeeMSFT added this to the 9.0.0 milestone Mar 29, 2024
@cshung

Copy link
Copy Markdown
Contributor

is there an API to check if an object is instantiated in a frozen heap

Yes, IGCHeap::IsInFrozenSegment should do.

virtual bool IsInFrozenSegment(Object *object) PURE_VIRTUAL

@xoofx

Copy link
Copy Markdown
MemberAuthor

Yes, IGCHeap::IsInFrozenSegment should do.

Thanks! I meant from C# as I'm not familiar how I will be able to make tests only from C++.

@jkotas

Copy link
Copy Markdown
Member

is there an API to check if an object is instantiated in a frozen heap

We prefer to test observable behavior like that the program does not crash. You do not need an API that checks if an object is instantiated in a frozen heap for that.

@jkotas

jkotas commented Mar 30, 2024

Copy link
Copy Markdown
Member

Yes, please, a starting place would be helpful!

This will crash with high probability due to this bug:

usingSystem.Runtime.Loader;publicclassProgram{staticvoidMain(){WeakReference[]wrs=newWeakReference[10];for(inti=0;i<wrs.Length;i++){varalc=newMyAssemblyLoadContext();vara=alc.LoadFromAssemblyPath(typeof(Program).Assembly.Location);wrs[i]=(WeakReference)a.GetType("Program").GetMethod("Work").Invoke(null,null);GC.Collect();}foreach(varwrinwrs){Console.WriteLine(wr.Target);}}publicstaticWeakReferenceWork(){returnnewWeakReference(Array.Empty<Program>());}}classMyAssemblyLoadContext:AssemblyLoadContext{publicMyAssemblyLoadContext():base(isCollectible:true){}}

@xoofx

Copy link
Copy Markdown
MemberAuthor

We prefer to test observable behavior like that the program does not crash. You do not need an API that checks if an object is instantiated in a frozen heap for that.
This will crash with high probability due to this bug:

Makes sense! Where should I add such a test? In src\tests\GC\Scenarios but I see also that there is a src\tests\GC\API\Frozen?

@jkotas

jkotas commented Mar 30, 2024

Copy link
Copy Markdown
Member

As I was writing the test, I have realized that there is an opposite problem too: The frozen object allocated in shared generic static constructor may end up leaking:

classMyG<T>{// This will be allocated on frozen heap, but it is going to leak if T is collectiblestaticobjects=newobject();}

I am not sure what's the best way to fix this leak. We can either detect and reject these problematic patterns as canidates for frozen heap allocation, or we need to pass the containing generic type to the MayBeFrozen allocation helper. cc @EgorBo

@jkotas

Copy link
Copy Markdown
Member

Where should I add such a test?

I would add it under src\tests\JIT\Regression\JitBlue. This is a codegen related problem, so the regression test for it belongs under JIT tests.

@xoofx

Copy link
Copy Markdown
MemberAuthor

Added the test with commit 1189eec but I don't know how to run it. I tried .\build.cmd clr+libs+libs.tests -rc checked -lc release -test but doesn't look that it ran it.

@EgorBo

EgorBo commented Mar 30, 2024

Copy link
Copy Markdown
Member

As I was writing the test, I have realized that there is an opposite problem too: The frozen object allocated in shared generic static constructor may end up leaking:

classMyG<T>{// This will be allocated on frozen heap, but it is going to leak if T is collectiblestaticobjects=newobject();}

I am not sure what's the best way to fix this leak. We can either detect and reject these problematic patterns as canidates for frozen heap allocation, or we need to pass the containing generic type to the MayBeFrozen allocation helper. cc @EgorBo

Good point! I propose this patch here:

- if ((fi.fieldFlags & flagsToCheck) == flagsToCheck)+ if (((fi.fieldFlags & flagsToCheck) == flagsToCheck) &&+ ((info.compCompHnd->getClassAttribs(info.compClassHnd) & CORINFO_FLG_SHAREDINST) == 0))

the logic to detect candidates for NonGC allocators is quite trivial here, I have a prototype for a more advanced version, but it's not ready yet.

@EgorBo

Copy link
Copy Markdown
Member

Good point! I propose this patch here:

Ah, it's a bit too conservative. Basically, with this patch we'll never optimize Array.Empty<string>() into a nongc handle. I presume a proper way is to pass type of current class as an argument in frozen allocator.. but that is not a trivial change so probably for now we should take the conservative patch?

@xoofx
xoofxforce-pushed the fix-frozen-empty-array-alloc-for-collectible-array-type branch from 6050688 to 81578a1CompareMarch 30, 2024 17:06
@jkotas

Copy link
Copy Markdown
Member

Ah, it's a bit too conservative. Basically, with this patch we'll never optimize Array.Empty() into a nongc handle. I presume a proper way is to pass type of current class as an argument in frozen allocator.. but that is not a trivial change so probably for now we should take the conservative patch?

Yes, the conservative fix can be backport candidate. The full fix would be hard to backport.

@xoofx Would you like to include it in this PR? (The test for this case can have similar structure - it should use weak handle to verify that the object is gone after the collectible assembly is unloaded.)

Comment threadsrc/tests/JIT/Regression/JitBlue/Runtime_100437/Runtime_100437.cs Outdated
Comment threadsrc/tests/JIT/Regression/JitBlue/Runtime_100437/Runtime_100437.cs Outdated
Comment threadsrc/tests/JIT/Regression/JitBlue/Runtime_100437/Runtime_100437.csproj Outdated
@xoofx
xoofxforce-pushed the fix-frozen-empty-array-alloc-for-collectible-array-type branch from 81578a1 to a9abd1dCompareMarch 30, 2024 17:58
Comment threadsrc/coreclr/vm/gchelpers.cpp Outdated
@jkotas

Copy link
Copy Markdown
Member

I have pushed test update that hits all interesting cases.

Comment threadsrc/tests/issues.targets Outdated
@EgorBo

Copy link
Copy Markdown
Member

I also usually struggle finding the right syntax for issues.props 😐

@jkotas
jkotas merged commit 59d9749 into dotnet:mainApr 2, 2024
@jkotas

Copy link
Copy Markdown
Member

/backport to release/8.0-staging

@github-actions

Copy link
Copy Markdown
Contributor

Started backporting to release/8.0-staging: https://github.com/dotnet/runtime/actions/runs/8517805878

@github-actions

Copy link
Copy Markdown
Contributor

@jkotas backporting to release/8.0-staging failed, the patch most likely resulted in conflicts:

$ git am --3way --ignore-whitespace --keep-non-patch changes.patch
Applying: Fix allocation of empty array in the frozen heap for collectible types (#100437)
Using index info to reconstruct a base tree...
M	src/coreclr/vm/frozenobjectheap.cpp
M	src/coreclr/vm/gchelpers.cpp
Falling back to patching base and 3-way merge...
Auto-merging src/coreclr/vm/gchelpers.cpp
CONFLICT (content): Merge conflict in src/coreclr/vm/gchelpers.cpp
Auto-merging src/coreclr/vm/frozenobjectheap.cpp
error: Failed to merge in the changes.
hint: Use 'git am --show-current-patch=diff' to see the failed patch
Patch failed at 0001 Fix allocation of empty array in the frozen heap for collectible types (#100437)
When you have resolved this problem, run "git am --continue".
If you prefer to skip this patch, run "git am --skip" instead.
To restore the original branch and stop patching, run "git am --abort".
Error: The process '/usr/bin/git' failed with exit code 128

Please backport manually!

@github-actions

Copy link
Copy Markdown
Contributor

@jkotas an error occurred while backporting to release/8.0-staging, please check the run log for details!

Error: git am failed, most likely due to a merge conflict.

jkotas added a commit that referenced this pull request Apr 2, 2024
#100444)
* Fix allocation of empty array in the frozen heap for collectible types (#100437)
* Remove Optimize from csproj
* Add test for generic with static
* Apply suggestions from code review
* Better test
* Disable tests on Mono
---------
Co-authored-by: Jan Kotas <jkotas@microsoft.com>
@jkotas

Copy link
Copy Markdown
Member

I have opened #100510 on re-enabling the optimization in shared generic with proper lifetime checks.

#100509 is the backport to .NET 8.

@xoofx Thank you for your help with getting this bug found and fixed!

@xoofx

xoofx commented Apr 2, 2024

Copy link
Copy Markdown
MemberAuthor

@xoofx Thank you for your help with getting this bug found and fixed!

Most of the hard work investigating the crash from our side came from @alexey-zakharov☺️

We are super glad that a quick fix was found, as we are heavily relying on collectible ALC and that bug haunted several crashes on our CI. Thanks a lot for helping with it!

alexey-zakharov pushed a commit to Unity-Technologies/runtime that referenced this pull request Apr 2, 2024
dotnet#100444)
* Fix allocation of empty array in the frozen heap for collectible types (dotnet#100437)
* Remove Optimize from csproj
* Add test for generic with static
* Apply suggestions from code review
* Better test
* Disable tests on Mono
---------
Co-authored-by: Jan Kotas <jkotas@microsoft.com>
(cherry picked from commit 78f7707)
jkotas added a commit that referenced this pull request Apr 4, 2024
#100444) (#100509)
* Fix allocation of empty array in the frozen heap for collectible types (#100437)
* Remove Optimize from csproj
* Add test for generic with static
* Apply suggestions from code review
* Better test
* Disable tests on Mono
---------
Co-authored-by: Alexandre Mutel <alexandre_mutel@live.com>
@xoofx
xoofx deleted the fix-frozen-empty-array-alloc-for-collectible-array-type branch April 11, 2024 09:52
matouskozak pushed a commit to matouskozak/runtime that referenced this pull request Apr 30, 2024
dotnet#100444)
* Fix allocation of empty array in the frozen heap for collectible types (dotnet#100437)
* Remove Optimize from csproj
* Add test for generic with static
* Apply suggestions from code review
* Better test
* Disable tests on Mono
---------
Co-authored-by: Jan Kotas <jkotas@microsoft.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators May 12, 2024
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMIcommunity-contributionIndicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Empty array allocated on the Frozen Heap for a Collectible type?

6 participants

@xoofx@jkotas@cshung@EgorBo@MichalPetryka@JulieLeeMSFT
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Fix allocation of empty array in the frozen heap for collectible types - #100444

Merged
jkotas merged 13 commits into
dotnet:mainfrom
xoofx:fix-frozen-empty-array-alloc-for-collectible-array-type
Apr 2, 2024
Merged

Fix allocation of empty array in the frozen heap for collectible types#100444
jkotas merged 13 commits into
dotnet:mainfrom
xoofx:fix-frozen-empty-array-alloc-for-collectible-array-type

Conversation

@xoofx

Copy link
Copy Markdown
Member

Fixes#100437

An empty array should not be allocated on a frozen heap if its type is coming from a collectible assembly.

Might be difficult to bring a proper test (e.g is there an API to check if an object is instantiated in a frozen heap?). If it is required, guidance appreciated.

This fix should be backported to net8.0 as well.

@ghostghost added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Mar 29, 2024
@dotnet-policy-servicedotnet-policy-serviceBot added the community-contribution Indicates that the PR has been added by a community member label Mar 29, 2024
Comment threadsrc/coreclr/vm/gchelpers.cpp Outdated
@xoofx

Copy link
Copy Markdown
MemberAuthor

My colleague that has been investigating this is also suggesting adding an assert in FrozenObjectHeapManager::TryAllocateObject to check that a type cannot be collectible.

_ASSERT(type != nullptr);
_ASSERT(FOH_COMMIT_SIZE >= MIN_OBJECT_SIZE);

Thoughts?

I can add it as part of this PR.

@jkotas

Copy link
Copy Markdown
Member

adding an assert

Sounds good to me.

We should also add a test that hits it.

@jkotas

Copy link
Copy Markdown
Member

We should also add a test that hits it.

Let me know if you need help with the test.

@xoofx

Copy link
Copy Markdown
MemberAuthor

Let me know if you need help with the test.

Yes, please, a starting place would be helpful! 😅

@JulieLeeMSFTJulieLeeMSFT added this to the 9.0.0 milestone Mar 29, 2024
@cshung

Copy link
Copy Markdown
Contributor

is there an API to check if an object is instantiated in a frozen heap

Yes, IGCHeap::IsInFrozenSegment should do.

virtual bool IsInFrozenSegment(Object *object) PURE_VIRTUAL

@xoofx

Copy link
Copy Markdown
MemberAuthor

Yes, IGCHeap::IsInFrozenSegment should do.

Thanks! I meant from C# as I'm not familiar how I will be able to make tests only from C++.

@jkotas

Copy link
Copy Markdown
Member

is there an API to check if an object is instantiated in a frozen heap

We prefer to test observable behavior like that the program does not crash. You do not need an API that checks if an object is instantiated in a frozen heap for that.

@jkotas

jkotas commented Mar 30, 2024

Copy link
Copy Markdown
Member

Yes, please, a starting place would be helpful!

This will crash with high probability due to this bug:

usingSystem.Runtime.Loader;publicclassProgram{staticvoidMain(){WeakReference[]wrs=newWeakReference[10];for(inti=0;i<wrs.Length;i++){varalc=newMyAssemblyLoadContext();vara=alc.LoadFromAssemblyPath(typeof(Program).Assembly.Location);wrs[i]=(WeakReference)a.GetType("Program").GetMethod("Work").Invoke(null,null);GC.Collect();}foreach(varwrinwrs){Console.WriteLine(wr.Target);}}publicstaticWeakReferenceWork(){returnnewWeakReference(Array.Empty<Program>());}}classMyAssemblyLoadContext:AssemblyLoadContext{publicMyAssemblyLoadContext():base(isCollectible:true){}}

@xoofx

Copy link
Copy Markdown
MemberAuthor

We prefer to test observable behavior like that the program does not crash. You do not need an API that checks if an object is instantiated in a frozen heap for that.
This will crash with high probability due to this bug:

Makes sense! Where should I add such a test? In src\tests\GC\Scenarios but I see also that there is a src\tests\GC\API\Frozen?

@jkotas

jkotas commented Mar 30, 2024

Copy link
Copy Markdown
Member

As I was writing the test, I have realized that there is an opposite problem too: The frozen object allocated in shared generic static constructor may end up leaking:

classMyG<T>{// This will be allocated on frozen heap, but it is going to leak if T is collectiblestaticobjects=newobject();}

I am not sure what's the best way to fix this leak. We can either detect and reject these problematic patterns as canidates for frozen heap allocation, or we need to pass the containing generic type to the MayBeFrozen allocation helper. cc @EgorBo

@jkotas

Copy link
Copy Markdown
Member

Where should I add such a test?

I would add it under src\tests\JIT\Regression\JitBlue. This is a codegen related problem, so the regression test for it belongs under JIT tests.

@xoofx

Copy link
Copy Markdown
MemberAuthor

Added the test with commit 1189eec but I don't know how to run it. I tried .\build.cmd clr+libs+libs.tests -rc checked -lc release -test but doesn't look that it ran it.

@EgorBo

EgorBo commented Mar 30, 2024

Copy link
Copy Markdown
Member

As I was writing the test, I have realized that there is an opposite problem too: The frozen object allocated in shared generic static constructor may end up leaking:

classMyG<T>{// This will be allocated on frozen heap, but it is going to leak if T is collectiblestaticobjects=newobject();}

I am not sure what's the best way to fix this leak. We can either detect and reject these problematic patterns as canidates for frozen heap allocation, or we need to pass the containing generic type to the MayBeFrozen allocation helper. cc @EgorBo

Good point! I propose this patch here:

- if ((fi.fieldFlags & flagsToCheck) == flagsToCheck)+ if (((fi.fieldFlags & flagsToCheck) == flagsToCheck) &&+ ((info.compCompHnd->getClassAttribs(info.compClassHnd) & CORINFO_FLG_SHAREDINST) == 0))

the logic to detect candidates for NonGC allocators is quite trivial here, I have a prototype for a more advanced version, but it's not ready yet.

@EgorBo

Copy link
Copy Markdown
Member

Good point! I propose this patch here:

Ah, it's a bit too conservative. Basically, with this patch we'll never optimize Array.Empty<string>() into a nongc handle. I presume a proper way is to pass type of current class as an argument in frozen allocator.. but that is not a trivial change so probably for now we should take the conservative patch?

@xoofx
xoofxforce-pushed the fix-frozen-empty-array-alloc-for-collectible-array-type branch from 6050688 to 81578a1CompareMarch 30, 2024 17:06
@jkotas

Copy link
Copy Markdown
Member

Ah, it's a bit too conservative. Basically, with this patch we'll never optimize Array.Empty() into a nongc handle. I presume a proper way is to pass type of current class as an argument in frozen allocator.. but that is not a trivial change so probably for now we should take the conservative patch?

Yes, the conservative fix can be backport candidate. The full fix would be hard to backport.

@xoofx Would you like to include it in this PR? (The test for this case can have similar structure - it should use weak handle to verify that the object is gone after the collectible assembly is unloaded.)

Comment threadsrc/tests/JIT/Regression/JitBlue/Runtime_100437/Runtime_100437.cs Outdated
Comment threadsrc/tests/JIT/Regression/JitBlue/Runtime_100437/Runtime_100437.cs Outdated
Comment threadsrc/tests/JIT/Regression/JitBlue/Runtime_100437/Runtime_100437.csproj Outdated
@xoofx
xoofxforce-pushed the fix-frozen-empty-array-alloc-for-collectible-array-type branch from 81578a1 to a9abd1dCompareMarch 30, 2024 17:58
Comment threadsrc/coreclr/vm/gchelpers.cpp Outdated
@jkotas

Copy link
Copy Markdown
Member

I have pushed test update that hits all interesting cases.

Comment threadsrc/tests/issues.targets Outdated
@EgorBo

Copy link
Copy Markdown
Member

I also usually struggle finding the right syntax for issues.props 😐

@jkotas
jkotas merged commit 59d9749 into dotnet:mainApr 2, 2024
@jkotas

Copy link
Copy Markdown
Member

/backport to release/8.0-staging

@github-actions

Copy link
Copy Markdown
Contributor

Started backporting to release/8.0-staging: https://github.com/dotnet/runtime/actions/runs/8517805878

@github-actions

Copy link
Copy Markdown
Contributor

@jkotas backporting to release/8.0-staging failed, the patch most likely resulted in conflicts:

$ git am --3way --ignore-whitespace --keep-non-patch changes.patch
Applying: Fix allocation of empty array in the frozen heap for collectible types (#100437)
Using index info to reconstruct a base tree...
M	src/coreclr/vm/frozenobjectheap.cpp
M	src/coreclr/vm/gchelpers.cpp
Falling back to patching base and 3-way merge...
Auto-merging src/coreclr/vm/gchelpers.cpp
CONFLICT (content): Merge conflict in src/coreclr/vm/gchelpers.cpp
Auto-merging src/coreclr/vm/frozenobjectheap.cpp
error: Failed to merge in the changes.
hint: Use 'git am --show-current-patch=diff' to see the failed patch
Patch failed at 0001 Fix allocation of empty array in the frozen heap for collectible types (#100437)
When you have resolved this problem, run "git am --continue".
If you prefer to skip this patch, run "git am --skip" instead.
To restore the original branch and stop patching, run "git am --abort".
Error: The process '/usr/bin/git' failed with exit code 128

Please backport manually!

@github-actions

Copy link
Copy Markdown
Contributor

@jkotas an error occurred while backporting to release/8.0-staging, please check the run log for details!

Error: git am failed, most likely due to a merge conflict.

jkotas added a commit that referenced this pull request Apr 2, 2024
#100444)
* Fix allocation of empty array in the frozen heap for collectible types (#100437)
* Remove Optimize from csproj
* Add test for generic with static
* Apply suggestions from code review
* Better test
* Disable tests on Mono
---------
Co-authored-by: Jan Kotas <jkotas@microsoft.com>
@jkotas

Copy link
Copy Markdown
Member

I have opened #100510 on re-enabling the optimization in shared generic with proper lifetime checks.

#100509 is the backport to .NET 8.

@xoofx Thank you for your help with getting this bug found and fixed!

@xoofx

xoofx commented Apr 2, 2024

Copy link
Copy Markdown
MemberAuthor

@xoofx Thank you for your help with getting this bug found and fixed!

Most of the hard work investigating the crash from our side came from @alexey-zakharov☺️

We are super glad that a quick fix was found, as we are heavily relying on collectible ALC and that bug haunted several crashes on our CI. Thanks a lot for helping with it!

alexey-zakharov pushed a commit to Unity-Technologies/runtime that referenced this pull request Apr 2, 2024
dotnet#100444)
* Fix allocation of empty array in the frozen heap for collectible types (dotnet#100437)
* Remove Optimize from csproj
* Add test for generic with static
* Apply suggestions from code review
* Better test
* Disable tests on Mono
---------
Co-authored-by: Jan Kotas <jkotas@microsoft.com>
(cherry picked from commit 78f7707)
jkotas added a commit that referenced this pull request Apr 4, 2024
#100444) (#100509)
* Fix allocation of empty array in the frozen heap for collectible types (#100437)
* Remove Optimize from csproj
* Add test for generic with static
* Apply suggestions from code review
* Better test
* Disable tests on Mono
---------
Co-authored-by: Alexandre Mutel <alexandre_mutel@live.com>
@xoofx
xoofx deleted the fix-frozen-empty-array-alloc-for-collectible-array-type branch April 11, 2024 09:52
matouskozak pushed a commit to matouskozak/runtime that referenced this pull request Apr 30, 2024
dotnet#100444)
* Fix allocation of empty array in the frozen heap for collectible types (dotnet#100437)
* Remove Optimize from csproj
* Add test for generic with static
* Apply suggestions from code review
* Better test
* Disable tests on Mono
---------
Co-authored-by: Jan Kotas <jkotas@microsoft.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators May 12, 2024
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMIcommunity-contributionIndicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Empty array allocated on the Frozen Heap for a Collectible type?

6 participants

@xoofx@jkotas@cshung@EgorBo@MichalPetryka@JulieLeeMSFT
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Fix allocation of empty array in the frozen heap for collectible types - #100444

Merged
jkotas merged 13 commits into
dotnet:mainfrom
xoofx:fix-frozen-empty-array-alloc-for-collectible-array-type
Apr 2, 2024
Merged

Fix allocation of empty array in the frozen heap for collectible types#100444
jkotas merged 13 commits into
dotnet:mainfrom
xoofx:fix-frozen-empty-array-alloc-for-collectible-array-type

Conversation

@xoofx

Copy link
Copy Markdown
Member

Fixes#100437

An empty array should not be allocated on a frozen heap if its type is coming from a collectible assembly.

Might be difficult to bring a proper test (e.g is there an API to check if an object is instantiated in a frozen heap?). If it is required, guidance appreciated.

This fix should be backported to net8.0 as well.

@ghostghost added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Mar 29, 2024
@dotnet-policy-servicedotnet-policy-serviceBot added the community-contribution Indicates that the PR has been added by a community member label Mar 29, 2024
Comment threadsrc/coreclr/vm/gchelpers.cpp Outdated
@xoofx

Copy link
Copy Markdown
MemberAuthor

My colleague that has been investigating this is also suggesting adding an assert in FrozenObjectHeapManager::TryAllocateObject to check that a type cannot be collectible.

_ASSERT(type != nullptr);
_ASSERT(FOH_COMMIT_SIZE >= MIN_OBJECT_SIZE);

Thoughts?

I can add it as part of this PR.

@jkotas

Copy link
Copy Markdown
Member

adding an assert

Sounds good to me.

We should also add a test that hits it.

@jkotas

Copy link
Copy Markdown
Member

We should also add a test that hits it.

Let me know if you need help with the test.

@xoofx

Copy link
Copy Markdown
MemberAuthor

Let me know if you need help with the test.

Yes, please, a starting place would be helpful! 😅

@JulieLeeMSFTJulieLeeMSFT added this to the 9.0.0 milestone Mar 29, 2024
@cshung

Copy link
Copy Markdown
Contributor

is there an API to check if an object is instantiated in a frozen heap

Yes, IGCHeap::IsInFrozenSegment should do.

virtual bool IsInFrozenSegment(Object *object) PURE_VIRTUAL

@xoofx

Copy link
Copy Markdown
MemberAuthor

Yes, IGCHeap::IsInFrozenSegment should do.

Thanks! I meant from C# as I'm not familiar how I will be able to make tests only from C++.

@jkotas

Copy link
Copy Markdown
Member

is there an API to check if an object is instantiated in a frozen heap

We prefer to test observable behavior like that the program does not crash. You do not need an API that checks if an object is instantiated in a frozen heap for that.

@jkotas

jkotas commented Mar 30, 2024

Copy link
Copy Markdown
Member

Yes, please, a starting place would be helpful!

This will crash with high probability due to this bug:

usingSystem.Runtime.Loader;publicclassProgram{staticvoidMain(){WeakReference[]wrs=newWeakReference[10];for(inti=0;i<wrs.Length;i++){varalc=newMyAssemblyLoadContext();vara=alc.LoadFromAssemblyPath(typeof(Program).Assembly.Location);wrs[i]=(WeakReference)a.GetType("Program").GetMethod("Work").Invoke(null,null);GC.Collect();}foreach(varwrinwrs){Console.WriteLine(wr.Target);}}publicstaticWeakReferenceWork(){returnnewWeakReference(Array.Empty<Program>());}}classMyAssemblyLoadContext:AssemblyLoadContext{publicMyAssemblyLoadContext():base(isCollectible:true){}}

@xoofx

Copy link
Copy Markdown
MemberAuthor

We prefer to test observable behavior like that the program does not crash. You do not need an API that checks if an object is instantiated in a frozen heap for that.
This will crash with high probability due to this bug:

Makes sense! Where should I add such a test? In src\tests\GC\Scenarios but I see also that there is a src\tests\GC\API\Frozen?

@jkotas

jkotas commented Mar 30, 2024

Copy link
Copy Markdown
Member

As I was writing the test, I have realized that there is an opposite problem too: The frozen object allocated in shared generic static constructor may end up leaking:

classMyG<T>{// This will be allocated on frozen heap, but it is going to leak if T is collectiblestaticobjects=newobject();}

I am not sure what's the best way to fix this leak. We can either detect and reject these problematic patterns as canidates for frozen heap allocation, or we need to pass the containing generic type to the MayBeFrozen allocation helper. cc @EgorBo

@jkotas

Copy link
Copy Markdown
Member

Where should I add such a test?

I would add it under src\tests\JIT\Regression\JitBlue. This is a codegen related problem, so the regression test for it belongs under JIT tests.

@xoofx

Copy link
Copy Markdown
MemberAuthor

Added the test with commit 1189eec but I don't know how to run it. I tried .\build.cmd clr+libs+libs.tests -rc checked -lc release -test but doesn't look that it ran it.

@EgorBo

EgorBo commented Mar 30, 2024

Copy link
Copy Markdown
Member

As I was writing the test, I have realized that there is an opposite problem too: The frozen object allocated in shared generic static constructor may end up leaking:

classMyG<T>{// This will be allocated on frozen heap, but it is going to leak if T is collectiblestaticobjects=newobject();}

I am not sure what's the best way to fix this leak. We can either detect and reject these problematic patterns as canidates for frozen heap allocation, or we need to pass the containing generic type to the MayBeFrozen allocation helper. cc @EgorBo

Good point! I propose this patch here:

- if ((fi.fieldFlags & flagsToCheck) == flagsToCheck)+ if (((fi.fieldFlags & flagsToCheck) == flagsToCheck) &&+ ((info.compCompHnd->getClassAttribs(info.compClassHnd) & CORINFO_FLG_SHAREDINST) == 0))

the logic to detect candidates for NonGC allocators is quite trivial here, I have a prototype for a more advanced version, but it's not ready yet.

@EgorBo

Copy link
Copy Markdown
Member

Good point! I propose this patch here:

Ah, it's a bit too conservative. Basically, with this patch we'll never optimize Array.Empty<string>() into a nongc handle. I presume a proper way is to pass type of current class as an argument in frozen allocator.. but that is not a trivial change so probably for now we should take the conservative patch?

@xoofx
xoofxforce-pushed the fix-frozen-empty-array-alloc-for-collectible-array-type branch from 6050688 to 81578a1CompareMarch 30, 2024 17:06
@jkotas

Copy link
Copy Markdown
Member

Ah, it's a bit too conservative. Basically, with this patch we'll never optimize Array.Empty() into a nongc handle. I presume a proper way is to pass type of current class as an argument in frozen allocator.. but that is not a trivial change so probably for now we should take the conservative patch?

Yes, the conservative fix can be backport candidate. The full fix would be hard to backport.

@xoofx Would you like to include it in this PR? (The test for this case can have similar structure - it should use weak handle to verify that the object is gone after the collectible assembly is unloaded.)

Comment threadsrc/tests/JIT/Regression/JitBlue/Runtime_100437/Runtime_100437.cs Outdated
Comment threadsrc/tests/JIT/Regression/JitBlue/Runtime_100437/Runtime_100437.cs Outdated
Comment threadsrc/tests/JIT/Regression/JitBlue/Runtime_100437/Runtime_100437.csproj Outdated
@xoofx
xoofxforce-pushed the fix-frozen-empty-array-alloc-for-collectible-array-type branch from 81578a1 to a9abd1dCompareMarch 30, 2024 17:58
Comment threadsrc/coreclr/vm/gchelpers.cpp Outdated
@jkotas

Copy link
Copy Markdown
Member

I have pushed test update that hits all interesting cases.

Comment threadsrc/tests/issues.targets Outdated
@EgorBo

Copy link
Copy Markdown
Member

I also usually struggle finding the right syntax for issues.props 😐

@jkotas
jkotas merged commit 59d9749 into dotnet:mainApr 2, 2024
@jkotas

Copy link
Copy Markdown
Member

/backport to release/8.0-staging

@github-actions

Copy link
Copy Markdown
Contributor

Started backporting to release/8.0-staging: https://github.com/dotnet/runtime/actions/runs/8517805878

@github-actions

Copy link
Copy Markdown
Contributor

@jkotas backporting to release/8.0-staging failed, the patch most likely resulted in conflicts:

$ git am --3way --ignore-whitespace --keep-non-patch changes.patch
Applying: Fix allocation of empty array in the frozen heap for collectible types (#100437)
Using index info to reconstruct a base tree...
M	src/coreclr/vm/frozenobjectheap.cpp
M	src/coreclr/vm/gchelpers.cpp
Falling back to patching base and 3-way merge...
Auto-merging src/coreclr/vm/gchelpers.cpp
CONFLICT (content): Merge conflict in src/coreclr/vm/gchelpers.cpp
Auto-merging src/coreclr/vm/frozenobjectheap.cpp
error: Failed to merge in the changes.
hint: Use 'git am --show-current-patch=diff' to see the failed patch
Patch failed at 0001 Fix allocation of empty array in the frozen heap for collectible types (#100437)
When you have resolved this problem, run "git am --continue".
If you prefer to skip this patch, run "git am --skip" instead.
To restore the original branch and stop patching, run "git am --abort".
Error: The process '/usr/bin/git' failed with exit code 128

Please backport manually!

@github-actions

Copy link
Copy Markdown
Contributor

@jkotas an error occurred while backporting to release/8.0-staging, please check the run log for details!

Error: git am failed, most likely due to a merge conflict.

jkotas added a commit that referenced this pull request Apr 2, 2024
#100444)
* Fix allocation of empty array in the frozen heap for collectible types (#100437)
* Remove Optimize from csproj
* Add test for generic with static
* Apply suggestions from code review
* Better test
* Disable tests on Mono
---------
Co-authored-by: Jan Kotas <jkotas@microsoft.com>
@jkotas

Copy link
Copy Markdown
Member

I have opened #100510 on re-enabling the optimization in shared generic with proper lifetime checks.

#100509 is the backport to .NET 8.

@xoofx Thank you for your help with getting this bug found and fixed!

@xoofx

xoofx commented Apr 2, 2024

Copy link
Copy Markdown
MemberAuthor

@xoofx Thank you for your help with getting this bug found and fixed!

Most of the hard work investigating the crash from our side came from @alexey-zakharov☺️

We are super glad that a quick fix was found, as we are heavily relying on collectible ALC and that bug haunted several crashes on our CI. Thanks a lot for helping with it!

alexey-zakharov pushed a commit to Unity-Technologies/runtime that referenced this pull request Apr 2, 2024
dotnet#100444)
* Fix allocation of empty array in the frozen heap for collectible types (dotnet#100437)
* Remove Optimize from csproj
* Add test for generic with static
* Apply suggestions from code review
* Better test
* Disable tests on Mono
---------
Co-authored-by: Jan Kotas <jkotas@microsoft.com>
(cherry picked from commit 78f7707)
jkotas added a commit that referenced this pull request Apr 4, 2024
#100444) (#100509)
* Fix allocation of empty array in the frozen heap for collectible types (#100437)
* Remove Optimize from csproj
* Add test for generic with static
* Apply suggestions from code review
* Better test
* Disable tests on Mono
---------
Co-authored-by: Alexandre Mutel <alexandre_mutel@live.com>
@xoofx
xoofx deleted the fix-frozen-empty-array-alloc-for-collectible-array-type branch April 11, 2024 09:52
matouskozak pushed a commit to matouskozak/runtime that referenced this pull request Apr 30, 2024
dotnet#100444)
* Fix allocation of empty array in the frozen heap for collectible types (dotnet#100437)
* Remove Optimize from csproj
* Add test for generic with static
* Apply suggestions from code review
* Better test
* Disable tests on Mono
---------
Co-authored-by: Jan Kotas <jkotas@microsoft.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators May 12, 2024
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMIcommunity-contributionIndicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Empty array allocated on the Frozen Heap for a Collectible type?

6 participants

@xoofx@jkotas@cshung@EgorBo@MichalPetryka@JulieLeeMSFT
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Fix allocation of empty array in the frozen heap for collectible types - #100444

Merged
jkotas merged 13 commits into
dotnet:mainfrom
xoofx:fix-frozen-empty-array-alloc-for-collectible-array-type
Apr 2, 2024
Merged

Fix allocation of empty array in the frozen heap for collectible types#100444
jkotas merged 13 commits into
dotnet:mainfrom
xoofx:fix-frozen-empty-array-alloc-for-collectible-array-type

Conversation

@xoofx

Copy link
Copy Markdown
Member

Fixes#100437

An empty array should not be allocated on a frozen heap if its type is coming from a collectible assembly.

Might be difficult to bring a proper test (e.g is there an API to check if an object is instantiated in a frozen heap?). If it is required, guidance appreciated.

This fix should be backported to net8.0 as well.

@ghostghost added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Mar 29, 2024
@dotnet-policy-servicedotnet-policy-serviceBot added the community-contribution Indicates that the PR has been added by a community member label Mar 29, 2024
Comment threadsrc/coreclr/vm/gchelpers.cpp Outdated
@xoofx

Copy link
Copy Markdown
MemberAuthor

My colleague that has been investigating this is also suggesting adding an assert in FrozenObjectHeapManager::TryAllocateObject to check that a type cannot be collectible.

_ASSERT(type != nullptr);
_ASSERT(FOH_COMMIT_SIZE >= MIN_OBJECT_SIZE);

Thoughts?

I can add it as part of this PR.

@jkotas

Copy link
Copy Markdown
Member

adding an assert

Sounds good to me.

We should also add a test that hits it.

@jkotas

Copy link
Copy Markdown
Member

We should also add a test that hits it.

Let me know if you need help with the test.

@xoofx

Copy link
Copy Markdown
MemberAuthor

Let me know if you need help with the test.

Yes, please, a starting place would be helpful! 😅

@JulieLeeMSFTJulieLeeMSFT added this to the 9.0.0 milestone Mar 29, 2024
@cshung

Copy link
Copy Markdown
Contributor

is there an API to check if an object is instantiated in a frozen heap

Yes, IGCHeap::IsInFrozenSegment should do.

virtual bool IsInFrozenSegment(Object *object) PURE_VIRTUAL

@xoofx

Copy link
Copy Markdown
MemberAuthor

Yes, IGCHeap::IsInFrozenSegment should do.

Thanks! I meant from C# as I'm not familiar how I will be able to make tests only from C++.

@jkotas

Copy link
Copy Markdown
Member

is there an API to check if an object is instantiated in a frozen heap

We prefer to test observable behavior like that the program does not crash. You do not need an API that checks if an object is instantiated in a frozen heap for that.

@jkotas

jkotas commented Mar 30, 2024

Copy link
Copy Markdown
Member

Yes, please, a starting place would be helpful!

This will crash with high probability due to this bug:

usingSystem.Runtime.Loader;publicclassProgram{staticvoidMain(){WeakReference[]wrs=newWeakReference[10];for(inti=0;i<wrs.Length;i++){varalc=newMyAssemblyLoadContext();vara=alc.LoadFromAssemblyPath(typeof(Program).Assembly.Location);wrs[i]=(WeakReference)a.GetType("Program").GetMethod("Work").Invoke(null,null);GC.Collect();}foreach(varwrinwrs){Console.WriteLine(wr.Target);}}publicstaticWeakReferenceWork(){returnnewWeakReference(Array.Empty<Program>());}}classMyAssemblyLoadContext:AssemblyLoadContext{publicMyAssemblyLoadContext():base(isCollectible:true){}}

@xoofx

Copy link
Copy Markdown
MemberAuthor

We prefer to test observable behavior like that the program does not crash. You do not need an API that checks if an object is instantiated in a frozen heap for that.
This will crash with high probability due to this bug:

Makes sense! Where should I add such a test? In src\tests\GC\Scenarios but I see also that there is a src\tests\GC\API\Frozen?

@jkotas

jkotas commented Mar 30, 2024

Copy link
Copy Markdown
Member

As I was writing the test, I have realized that there is an opposite problem too: The frozen object allocated in shared generic static constructor may end up leaking:

classMyG<T>{// This will be allocated on frozen heap, but it is going to leak if T is collectiblestaticobjects=newobject();}

I am not sure what's the best way to fix this leak. We can either detect and reject these problematic patterns as canidates for frozen heap allocation, or we need to pass the containing generic type to the MayBeFrozen allocation helper. cc @EgorBo

@jkotas

Copy link
Copy Markdown
Member

Where should I add such a test?

I would add it under src\tests\JIT\Regression\JitBlue. This is a codegen related problem, so the regression test for it belongs under JIT tests.

@xoofx

Copy link
Copy Markdown
MemberAuthor

Added the test with commit 1189eec but I don't know how to run it. I tried .\build.cmd clr+libs+libs.tests -rc checked -lc release -test but doesn't look that it ran it.

@EgorBo

EgorBo commented Mar 30, 2024

Copy link
Copy Markdown
Member

As I was writing the test, I have realized that there is an opposite problem too: The frozen object allocated in shared generic static constructor may end up leaking:

classMyG<T>{// This will be allocated on frozen heap, but it is going to leak if T is collectiblestaticobjects=newobject();}

I am not sure what's the best way to fix this leak. We can either detect and reject these problematic patterns as canidates for frozen heap allocation, or we need to pass the containing generic type to the MayBeFrozen allocation helper. cc @EgorBo

Good point! I propose this patch here:

- if ((fi.fieldFlags & flagsToCheck) == flagsToCheck)+ if (((fi.fieldFlags & flagsToCheck) == flagsToCheck) &&+ ((info.compCompHnd->getClassAttribs(info.compClassHnd) & CORINFO_FLG_SHAREDINST) == 0))

the logic to detect candidates for NonGC allocators is quite trivial here, I have a prototype for a more advanced version, but it's not ready yet.

@EgorBo

Copy link
Copy Markdown
Member

Good point! I propose this patch here:

Ah, it's a bit too conservative. Basically, with this patch we'll never optimize Array.Empty<string>() into a nongc handle. I presume a proper way is to pass type of current class as an argument in frozen allocator.. but that is not a trivial change so probably for now we should take the conservative patch?

@xoofx
xoofxforce-pushed the fix-frozen-empty-array-alloc-for-collectible-array-type branch from 6050688 to 81578a1CompareMarch 30, 2024 17:06
@jkotas

Copy link
Copy Markdown
Member

Ah, it's a bit too conservative. Basically, with this patch we'll never optimize Array.Empty() into a nongc handle. I presume a proper way is to pass type of current class as an argument in frozen allocator.. but that is not a trivial change so probably for now we should take the conservative patch?

Yes, the conservative fix can be backport candidate. The full fix would be hard to backport.

@xoofx Would you like to include it in this PR? (The test for this case can have similar structure - it should use weak handle to verify that the object is gone after the collectible assembly is unloaded.)

Comment threadsrc/tests/JIT/Regression/JitBlue/Runtime_100437/Runtime_100437.cs Outdated
Comment threadsrc/tests/JIT/Regression/JitBlue/Runtime_100437/Runtime_100437.cs Outdated
Comment threadsrc/tests/JIT/Regression/JitBlue/Runtime_100437/Runtime_100437.csproj Outdated
@xoofx
xoofxforce-pushed the fix-frozen-empty-array-alloc-for-collectible-array-type branch from 81578a1 to a9abd1dCompareMarch 30, 2024 17:58
Comment threadsrc/coreclr/vm/gchelpers.cpp Outdated
@jkotas

Copy link
Copy Markdown
Member

I have pushed test update that hits all interesting cases.

Comment threadsrc/tests/issues.targets Outdated
@EgorBo

Copy link
Copy Markdown
Member

I also usually struggle finding the right syntax for issues.props 😐

@jkotas
jkotas merged commit 59d9749 into dotnet:mainApr 2, 2024
@jkotas

Copy link
Copy Markdown
Member

/backport to release/8.0-staging

@github-actions

Copy link
Copy Markdown
Contributor

Started backporting to release/8.0-staging: https://github.com/dotnet/runtime/actions/runs/8517805878

@github-actions

Copy link
Copy Markdown
Contributor

@jkotas backporting to release/8.0-staging failed, the patch most likely resulted in conflicts:

$ git am --3way --ignore-whitespace --keep-non-patch changes.patch
Applying: Fix allocation of empty array in the frozen heap for collectible types (#100437)
Using index info to reconstruct a base tree...
M	src/coreclr/vm/frozenobjectheap.cpp
M	src/coreclr/vm/gchelpers.cpp
Falling back to patching base and 3-way merge...
Auto-merging src/coreclr/vm/gchelpers.cpp
CONFLICT (content): Merge conflict in src/coreclr/vm/gchelpers.cpp
Auto-merging src/coreclr/vm/frozenobjectheap.cpp
error: Failed to merge in the changes.
hint: Use 'git am --show-current-patch=diff' to see the failed patch
Patch failed at 0001 Fix allocation of empty array in the frozen heap for collectible types (#100437)
When you have resolved this problem, run "git am --continue".
If you prefer to skip this patch, run "git am --skip" instead.
To restore the original branch and stop patching, run "git am --abort".
Error: The process '/usr/bin/git' failed with exit code 128

Please backport manually!

@github-actions

Copy link
Copy Markdown
Contributor

@jkotas an error occurred while backporting to release/8.0-staging, please check the run log for details!

Error: git am failed, most likely due to a merge conflict.

jkotas added a commit that referenced this pull request Apr 2, 2024
#100444)
* Fix allocation of empty array in the frozen heap for collectible types (#100437)
* Remove Optimize from csproj
* Add test for generic with static
* Apply suggestions from code review
* Better test
* Disable tests on Mono
---------
Co-authored-by: Jan Kotas <jkotas@microsoft.com>
@jkotas

Copy link
Copy Markdown
Member

I have opened #100510 on re-enabling the optimization in shared generic with proper lifetime checks.

#100509 is the backport to .NET 8.

@xoofx Thank you for your help with getting this bug found and fixed!

@xoofx

xoofx commented Apr 2, 2024

Copy link
Copy Markdown
MemberAuthor

@xoofx Thank you for your help with getting this bug found and fixed!

Most of the hard work investigating the crash from our side came from @alexey-zakharov☺️

We are super glad that a quick fix was found, as we are heavily relying on collectible ALC and that bug haunted several crashes on our CI. Thanks a lot for helping with it!

alexey-zakharov pushed a commit to Unity-Technologies/runtime that referenced this pull request Apr 2, 2024
dotnet#100444)
* Fix allocation of empty array in the frozen heap for collectible types (dotnet#100437)
* Remove Optimize from csproj
* Add test for generic with static
* Apply suggestions from code review
* Better test
* Disable tests on Mono
---------
Co-authored-by: Jan Kotas <jkotas@microsoft.com>
(cherry picked from commit 78f7707)
jkotas added a commit that referenced this pull request Apr 4, 2024
#100444) (#100509)
* Fix allocation of empty array in the frozen heap for collectible types (#100437)
* Remove Optimize from csproj
* Add test for generic with static
* Apply suggestions from code review
* Better test
* Disable tests on Mono
---------
Co-authored-by: Alexandre Mutel <alexandre_mutel@live.com>
@xoofx
xoofx deleted the fix-frozen-empty-array-alloc-for-collectible-array-type branch April 11, 2024 09:52
matouskozak pushed a commit to matouskozak/runtime that referenced this pull request Apr 30, 2024
dotnet#100444)
* Fix allocation of empty array in the frozen heap for collectible types (dotnet#100437)
* Remove Optimize from csproj
* Add test for generic with static
* Apply suggestions from code review
* Better test
* Disable tests on Mono
---------
Co-authored-by: Jan Kotas <jkotas@microsoft.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators May 12, 2024
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMIcommunity-contributionIndicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Empty array allocated on the Frozen Heap for a Collectible type?

6 participants

@xoofx@jkotas@cshung@EgorBo@MichalPetryka@JulieLeeMSFT