-
Notifications
You must be signed in to change notification settings - Fork 4.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Change PathInternal.IsCaseSensitive to a constant #54340
Conversation
I couldn't figure out the best area label to add to this PR. If you have write-permissions please help me learn by adding exactly one area label. |
Tagging subscribers to this area: @dotnet/area-system-io Issue DetailsSee #54313 (comment) and dotnet/maui#822 (comment) The code that initializes We should simply return true here and assume case sensitivity. Fixes #54339
|
Would this make sense to hardcode this for all mobile targets ? (e.g. have two files - |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would make sense to hardcode this for known behaviour (e.g. iOS like or browser-wasm).
I'm not sure why there is similar logic in Path class as well
internal static StringComparison StringComparison => |
src/libraries/Common/src/System/IO/PathInternal.CaseSensitivity.Android.cs
Outdated
Show resolved
Hide resolved
The behavior is actually hardcoded in Path class for all platforms:
The reason for this duplication is that there is no public We should make the logic in PathInternal to be hardcoded in the same way as the logic in Path. It helps with both performance and consistency. The check in PathInternal will need to be dynamic (using |
I'm following along with most of the discussion, but I don't understand this part @jkotas:
Aren't we talking about doing the same thing in PathInternal as in Path: just hardcode based on the common behavior of the underlying OS? Why would it need to be a dynamic check if we know which RID we're targeting? |
@lambdageek because the file is shared as a compilation file you cannot assume that the assembly where the file is included is multi-targeting (has RID specific builds). OperatingSystem properties are static constants so I don't think there will be any perf hit. |
03b034c
to
36740cb
Compare
Ok, updated to return constants for some known |
…orms In particular on Android probing using I/O is slow and contributes to slow app startup. Fixes dotnet#54339
36740cb
to
c604c47
Compare
I do not think we should be doing any probing. The IsCaseInsensitive property on Path is not doing any probing, and it better be in sync with the property on PathInternal. The probing on PathInternal can only ever lead to inconsistent behavior. |
src/libraries/Common/src/System/IO/PathInternal.CaseSensitivity.cs
Outdated
Show resolved
Hide resolved
#if NET6_0_OR_GREATER | ||
// Return a constant for mobile platforms without resorting to I/O | ||
if (OperatingSystem.IsMacOS() || OperatingSystem.IsMacCatalyst() || OperatingSystem.IsIOS() || OperatingSystem.IsTvOS() || OperatingSystem.IsWatchOS()) | ||
return true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is backwards - it should return false for MacOS.
{ | ||
#if NET6_0_OR_GREATER | ||
// Return a constant for mobile platforms without resorting to I/O | ||
if (OperatingSystem.IsMacOS() || OperatingSystem.IsMacCatalyst() || OperatingSystem.IsIOS() || OperatingSystem.IsTvOS() || OperatingSystem.IsWatchOS()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The ifdef in Path.Unix.cs does not have WatchOS. Is it a bug?
/// <remarks> | ||
/// Ideally we'd use something like pathconf with _PC_CASE_SENSITIVE, but that is non-portable, | ||
/// not supported on Windows or Linux, etc. For now, this function creates a tmp file with capital letters | ||
/// and then tests for its existence with lower-case letters. This could return invalid results in corner | ||
/// cases where, for example, different file systems are mounted with differing sensitivities. | ||
/// </remarks> | ||
private static bool GetIsCaseSensitive() | ||
private static bool GetIsCaseSensitiveByProbing() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that it would be great to use the old implementation as a test of the new one.
You could introduce a new test in System.IO.FileSystem.Tests.csproj include the .cs
file like this:
<Compile Include="$(CommonPath)System\IO\PathInternal.CaseSensitivity.cs"
Link="Common\System\IO\PathInternal.CaseSensitivity.cs" />
and move GetIsCaseSensitiveByProbing
there to get the "expected".
Something like this:
[Fact]
public void IsCaseSensitiveReturnsSameValueAsProbing()
{
Assert.Equal(GetIsCaseSensitiveByProbing(), PathInternal.IsCaseSensitive);
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That would be unreliable test. The temp directory on Unix may live on a case insensensitive volume.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That would be unreliable test. The temp directory on Unix may live on a case insensensitive volume.
So perhaps it should be using current directory instead of temp? I know that it would still be far from perfect but I would prefer to have a single non-perfect test rather than no tests at all.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Current directory has the same problem. We can have a test like that, but it should be marked as outerloop.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can have a test like that, but it should be marked as outerloop.
Sounds good to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So perhaps it should be using current directory instead of temp
That might fail on mobile for trivial reasons - I'm not sure the tests run from a writable directory. I'll give it a try and see.
Co-authored-by: Jan Kotas <jkotas@microsoft.com>
Also Path.StringComparison => PathInternal.StringComparison
Move GetIsCaseSensitiveByProbing to FileSystemTest
/// not supported on Windows or Linux, etc. For now, this function creates a tmp file with capital letters | ||
/// and then tests for its existence with lower-case letters. This could return invalid results in corner | ||
/// cases where, for example, different file systems are mounted with differing sensitivities. | ||
/// </remarks> | ||
private static bool GetIsCaseSensitive() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be internal static bool IsCaseSensitive
property and s_isCaseSensitive
should be deleted. It is not profitable to cache it in a static, and it prevents the linker constant propagation from kicking in.
@@ -957,11 +957,11 @@ private static string GetRelativePath(string relativeTo, string path, StringComp | |||
return sb.ToString(); | |||
} | |||
|
|||
/// <summary>Gets whether the system is case-sensitive.</summary> | |||
internal static bool IsCaseSensitive => PathInternal.IsCaseSensitive; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would change the few places that use this to call PathInternal.IsCaseSensitive
directly instead of introducing this wrapper.
} | ||
catch | ||
{ | ||
// In case something goes wrong (e.g. temp pointing to a privilieged directory), we don't |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This catch
made sense when it was part of the shipping code. Now that it is test, I think it would be better to delete it. We want to know when this fails as a test.
update callers to use PathInternal.IsCaseSensitive and PathInternal.StringComparison
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thank you @lambdageek !
Should we backport this to .NET 6 Preview 6? (It's not critical for MAUI on Android - they mitigated the perf hit on their end) |
Co-authored-by: Stephen Toub <stoub@microsoft.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks.
If the perf issue was mitigated, I don't think we need to. |
/azp run runtime-staging runtime-dev-innerloop dotnet-linker-tests |
No pipelines are associated with this pull request. |
…nitial_config * origin/main: (362 commits) [wasm][debugger] Reuse debugger-agent on wasm debugger (dotnet#52300) Put Crossgen2 in sync with dotnet#54235 (dotnet#54438) Move System.Object serialization to ObjectConverter (dotnet#54436) Move setting fHasVirtualStaticMethods out of sanity check section (dotnet#54574) [wasm] Compile .bc->.o in parallel, before passing to the linker (dotnet#54053) Change PathInternal.IsCaseSensitive to a constant (dotnet#54340) Make mono_polling_required a public symbol (dotnet#54592) Respect EventSource::IsSupported setting in more codepaths (dotnet#51977) Root ComActivator for hosting (dotnet#54524) Add ILLink annotations to S.D.Common related to DbConnectionStringBuilder (dotnet#54280) Fix finalizer issue with regions (dotnet#54550) Add support for multi-arch install locations (dotnet#53763) Update library testing docs page to reduce confusion (dotnet#54324) [FileStream] handle UNC and device paths (dotnet#54483) Update NetAnalyzers version (dotnet#54511) Added runtime dependency to fix the intermittent test failures (dotnet#54587) Disable failing System.Reflection.Tests.ModuleTests.GetMethods (dotnet#54564) [wasm] Move AOT builds from `runtime-staging` to `runtime` (dotnet#54577) Keep obj node for ArrayIndex. (dotnet#54584) Disable another failing MemoryCache test (dotnet#54578) ...
See #54313 (comment) and dotnet/maui#822 (comment)
The code that initialized
System.IO.PathInternal.s_isCaseSensitive
did I/O in the temp folder to check for case sensitivity. This is a startup perf hit on Android where low-end devices may have very slow storage. It also returns results that may be misleading since different paths on Unix may have different filesystems mounted that have different case sensitivity (a common case on Android is ext4 for internal storage and exFAT for the SD card).Instead, return a constant value based on the common platform behavior - case insensitive on Windows and Apple platforms, case sensitive elsewhere.
Also remove
Path.IsCaseSensitive
andPath.StringComparison
- callers should usePathInternal
properties directly.Fixes #54339