Uh oh!
There was an error while loading. Please reload this page.
Fix Issue #21678: Add Path.Exists -- to replace inefficient FileExists || DirectoryExists - #64347
Conversation
ghost
commented
Jan 26, 2022
Note regarding the This serves as a reminder for when your PR is modifying a ref *.cs file and adding/modifying public APIs, to please make sure the API implementation in the src *.cs file is documented with triple slash comments, so the PR reviewers can sign off that change. |
ghost
commented
Jan 26, 2022
Tagging subscribers to this area: @dotnet/area-system-io Issue DetailsAdded a new API
|
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
danmoseley
commented
Jan 26, 2022
thanks, I added some comments. |
danmoseley
commented
Jan 27, 2022
BTW @Tarun047 I'm guessing you're running on Windows and relying on PR validation to run the Unix tests. What many of us do that work on Windows is build and run tests on WSL2 (any distro eg., Ubuntu). It works really well. What I find easiest is to clone to my WSL2 distro exactly as I would on Windows, and set up the remote to my fork as I would on Windows, then push and pull via that to share bits. But some people set up remotes directly between Windows and WSL2. Once you have a clone, you just follow the regular doc in this repo for building and running tests on Linux. Given you've already created a PR for your branch here, if I wanted to share bits between the two without kicking validation again, I would push from one to a new branch, and pull that branch down to the other. Verify tests pass in both, then push that branch to this PR branch. |
danmoseley
commented
Jan 27, 2022
(Because relying on PR validation to run the tests for you makes development slow and noisy. We generally use PR validation to ensure that all the configuration matrix is tested, once we've run the key configuration(s) locally.) |
Tarun047
commented
Jan 27, 2022
Yes @danmoseley I am using Windows machine for the primary development, I do have a M1 Macbook pro, but haven't found time to setup the build infrastructure on it yet, hopefully will do it soon. |
danmoseley
commented
Jan 27, 2022
Cool, yes, I think you'd find it more productive for you and also less time burned on validation (which I think consumes 50-100 machines each push) 😸 |
adamsitnik
left a comment
There was a problem hiding this comment.
@Tarun047 big thanks for your contribution!
I've added few comments, mostly style but also about adding triple slash comments.
When adding the triple slash comments you can use some of the existing text from https://docs.microsoft.com/en-us/dotnet/api/system.io.file.exists?view=net-6.0
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
adamsitnik
commented
Jan 28, 2022
FWIW I've benchmarked the two sys-calls that allow us to check whether given file exists or not and the current implementation of the PR is already using the optimal one. 👍
Details<ProjectSdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<PackageReferenceInclude="BenchmarkDotNet"Version="0.13.1" />
</ItemGroup>
</Project>usingBenchmarkDotNet.Attributes;usingBenchmarkDotNet.Running;usingMicrosoft.Win32.SafeHandles;usingSystem;usingSystem.IO;usingSystem.Runtime.InteropServices;namespaceFileExistsWindowsSyscalls{internalclassProgram{staticvoidMain(string[]args)=>BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);}publicclassSyscalls{string_path;[Params(true,false)]publicboolIsFile;[Params(true,false)]publicboolExists;[GlobalSetup]publicvoidSetup(){_path=Path.Combine(Path.GetTempPath(),Path.GetRandomFileName());if(Exists){if(IsFile)File.Create(_path).Dispose();elseDirectory.CreateDirectory(_path);}}[GlobalCleanup]publicvoidCleanup(){if(Exists){if(IsFile)File.Delete(_path);elseDirectory.Delete(_path);}}[Benchmark(Baseline=true)]publicboolGetFileAttributesEx(){WIN32_FILE_ATTRIBUTE_DATAdata=default;returnGetFileAttributesEx(_path,GET_FILEEX_INFO_LEVELS.GetFileExInfoStandard,refdata);}[Benchmark]publicboolFindFirstFile(){WIN32_FIND_DATAdata=default;usingSafeFindHandlehandle=FindFirstFileEx(_path,FINDEX_INFO_LEVELS.FindExInfoBasic,refdata,FINDEX_SEARCH_OPS.FindExSearchNameMatch,IntPtr.Zero,0);return!handle.IsInvalid;}[DllImport("kernel32.dll",EntryPoint="GetFileAttributesExW",CharSet=CharSet.Unicode,ExactSpelling=true,SetLastError=true)]privatestaticexternboolGetFileAttributesEx(stringname,GET_FILEEX_INFO_LEVELSfileInfoLevel,refWIN32_FILE_ATTRIBUTE_DATAlpFileInformation);[DllImport("kernel32.dll",EntryPoint="FindFirstFileExW",CharSet=CharSet.Unicode,ExactSpelling=true,SetLastError=true)]privatestaticexternSafeFindHandleFindFirstFileEx(stringlpFileName,FINDEX_INFO_LEVELSfInfoLevelId,refWIN32_FIND_DATAlpFindFileData,FINDEX_SEARCH_OPSfSearchOp,IntPtrlpSearchFilter,intdwAdditionalFlags);}internalenumGET_FILEEX_INFO_LEVELS:uint{GetFileExInfoStandard=0x0u,GetFileExMaxInfoLevel=0x1u,}internalstructFILE_TIME{internaluintdwLowDateTime;internaluintdwHighDateTime;internalFILE_TIME(longfileTime){dwLowDateTime=(uint)fileTime;dwHighDateTime=(uint)(fileTime>>32);}internallongToTicks()=>((long)dwHighDateTime<<32)+dwLowDateTime;internalDateTimeToDateTimeUtc()=>DateTime.FromFileTimeUtc(ToTicks());internalDateTimeOffsetToDateTimeOffset()=>DateTimeOffset.FromFileTime(ToTicks());}[StructLayout(LayoutKind.Sequential,CharSet=CharSet.Unicode)]internalunsafestructWIN32_FIND_DATA{internalconstintMAX_PATH=260;internaluintdwFileAttributes;internalFILE_TIMEftCreationTime;internalFILE_TIMEftLastAccessTime;internalFILE_TIMEftLastWriteTime;internaluintnFileSizeHigh;internaluintnFileSizeLow;internaluintdwReserved0;internaluintdwReserved1;privatefixedchar_cFileName[MAX_PATH];privatefixedchar_cAlternateFileName[14];internalReadOnlySpan<char>cFileName{get{ fixed (char*c=_cFileName)returnnewReadOnlySpan<char>(c,MAX_PATH);}}}internalstructWIN32_FILE_ATTRIBUTE_DATA{internalintdwFileAttributes;internalFILE_TIMEftCreationTime;internalFILE_TIMEftLastAccessTime;internalFILE_TIMEftLastWriteTime;internaluintnFileSizeHigh;internaluintnFileSizeLow;internalvoidPopulateFrom(refWIN32_FIND_DATAfindData){dwFileAttributes=(int)findData.dwFileAttributes;ftCreationTime=findData.ftCreationTime;ftLastAccessTime=findData.ftLastAccessTime;ftLastWriteTime=findData.ftLastWriteTime;nFileSizeHigh=findData.nFileSizeHigh;nFileSizeLow=findData.nFileSizeLow;}}internalenumFINDEX_INFO_LEVELS:uint{FindExInfoStandard=0x0u,FindExInfoBasic=0x1u,FindExInfoMaxInfoLevel=0x2u,}internalenumFINDEX_SEARCH_OPS:uint{FindExSearchNameMatch=0x0u,FindExSearchLimitToDirectories=0x1u,FindExSearchLimitToDevices=0x2u,FindExSearchMaxSearchOp=0x3u,}internalsealedclassSafeFindHandle:SafeHandleZeroOrMinusOneIsInvalid{publicSafeFindHandle():base(true){}protectedoverrideboolReleaseHandle()=>FindClose(handle);[DllImport("kernel32.dll",SetLastError=true)]internalstaticexternboolFindClose(IntPtrhFindFile);}} |
adamsitnik
left a comment
There was a problem hiding this comment.
I've added some suggestions for how we could improve the docs. PTAL.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Co-authored-by: Adam Sitnik <adam.sitnik@gmail.com>
adamsitnik
left a comment
There was a problem hiding this comment.
Looks very good to me! Thank you for your contribution @Tarun047 !
danmoseley
commented
Jan 31, 2022
@Tarun047 we have more issues marked up for grabs - perhaps there is another that is interesting to you? |
Tarun047
commented
Feb 1, 2022
Sure |
Added a new API
Path.Exists(...)as per the approved spec described in issue #21678.This API is effectively equivalent to checking
File.Exists(...)combined withDirectory.Exists(...)Using this API we only make one syscall to the kernel, which would have been two calls otherwise.