Repository navigation
Remove a number of calls to Unsafe.AsPointer #99144
Copy link
Copy link
Open
Milestone
Description
There are a number of unnecessary uses of Unsafe.AsPointer in this repository (e.g.
| _EnumerateConfigurationValues(Unsafe.AsPointer(ref context), &ConfigCallback); |
| (byte*)Unsafe.AsPointer(ref _blocks[0]), // This is safe to do since we are a ref struct |
Most of the uses can be broken in to 2 categories:
- Aligning or detecting alignment of a reference (we can introduce helper APIs for this, e.g. )
nuint misalignedElements = ((nuint)Unsafe.AsPointer(ref searchSpace) & (nuint)(Vector256<byte>.Count - 1)) / (nuint)sizeof(T); - Getting a pointer to something that is known to be pinned (many of these cases could be converted to ref fields instead, using refs/spans directly, or passing by pointer, e.g. )
runtime/src/mono/System.Private.CoreLib/src/System/Reflection/TypeNameParser.Mono.cs
Line 48 in eaa6f01
_stackMark = Unsafe.AsPointer(ref stackMark)
The idea was mentioned by @tannergooding on the discord, and one of the suggested solutions was to create an internal API bool IsOpportunisticallyAligned(ref T address, nuint aligment) or similar for the alignment uses.
I am happy to make PRs for these. @tannergooding suggested that I make one for each area separately. Just making this issue to track it and get support for the general idea from people.
Activity
tannergooding commentedon Mar 1, 2024
I'm generally supportive of removing unsafe code in favor of safer patterns, particularly where newer features like
ref fieldscan be used to avoid the issue or special helpers like somebool IsOpportunisticallyAligned(ref T address, nuint aligment)could exist to document the intent and centralize the logic.Notably a lot of these places where its being used are fairly core APIs, so there is risk in making changes to them even if to remove unsafe code. It's something that we need to be extra cautious around and ensure gets the right scrutiny.
hamarb123 commentedon Mar 1, 2024
List of areas with calls to
Unsafe.AsPointer(tick indicates it's been reviewed and/or had a PR made for it):jkotas commentedon Mar 1, 2024
Why do you think that this use is unnecessary? What would you replace it with (while preserving the perf characteristics). I do not see a problem with this one.
No much different from every other
Unsafe.*API. You have to know what you are doing when usingUnsafe.*APIs.Sounds good to me. I see ~5 places where this can be used.
I am not convinced that it is that easy. Many of these uses have to do with low-level interop.
hamarb123 commentedon Mar 1, 2024
_EnumerateConfigurationValues(&context, &ConfigCallback);jkotas commentedon Mar 1, 2024
Ok this works but it requires pragma disable.
hamarb123 commentedon Mar 1, 2024
Which file/type should I define this on?
jkotas commentedon Mar 1, 2024
It can be internal method on
System.Runtime.CompilerServices.UnsafeMichalPetryka commentedon Mar 1, 2024
Do we want to disable it globally like it was mentioned in #80484 (review)?
tannergooding commentedon Mar 1, 2024
This issue is itself about trying to reduce unsafe patterns where unnecessary. Globally suppressing the warning that you're taking a pointer to a managed object is kind-of counter intuitive to that ;)
I think we want to be explicit when we're taking a pointer to a managed object to help reduce risk and make it very apparent we're taking the necessary approaches to keep the underlying object alive, etc.
sharwell commentedon Mar 4, 2024
In my opinion,
Unsafe.AsPointeris more clear than a disabled CS8500 combined with the&operator. One of the reasons why I prefer it is the disabled CS8500 applies to entire lines of code between the disable/restore, while the use ofUnsafe.AsPointerapplies only to the exact call where it was needed.sharwell commentedon Mar 4, 2024
It's also not clear to me how the changes proposed here would have prevented the GC hole mentioned in the original post above.
hamarb123 commentedon Mar 4, 2024
CS8500 is a bit of a silly warning anyway imo. The issue with
Unsafe.AsPointeris that it will happily allow unsafe cases where the memory is not fixed, whereas&will not.e.g., consider I have this:
if I moved (or otherwise have a)
vanywhere that's not obviously on the stack I will get an error, whereasUnsafe.AsPointerwill happily continue to "work" even when not pinned otherwise.If we avoid using
Unsafe.AsPointerwithout a good reason that other alternatives couldn't work, then it would have been more likely to have been questioned and caught.