Skip to content
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

Reduce allocations for CreateDirectory #61777

Merged
merged 7 commits into from
Nov 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,20 @@

using System;
using System.Runtime.InteropServices;
using System.Text;

internal static partial class Interop
{
internal static partial class Sys
{
[GeneratedDllImport(Libraries.SystemNative, EntryPoint = "SystemNative_MkDir", CharSet = CharSet.Ansi, SetLastError = true)]
adamsitnik marked this conversation as resolved.
Show resolved Hide resolved
internal static partial int MkDir(string path, int mode);
[GeneratedDllImport(Libraries.SystemNative, EntryPoint = "SystemNative_MkDir", SetLastError = true)]
private static partial int MkDir(ref byte path, int mode);
adamsitnik marked this conversation as resolved.
Show resolved Hide resolved

internal static int MkDir(ReadOnlySpan<char> path, int mode)
{
using ValueUtf8Converter converter = new(stackalloc byte[DefaultPathBufferSize]);
int result = MkDir(ref MemoryMarshal.GetReference(converter.ConvertAndTerminateString(path)), mode);
return result;
}
}
}
4 changes: 1 addition & 3 deletions src/libraries/System.IO.Pipes/src/System.IO.Pipes.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,6 @@
Link="Common\Interop\Unix\Interop.GetHostName.cs" />
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.GetPeerUserName.cs"
Link="Common\Interop\Unix\Interop.GetPeerUserName.cs" />
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.MkDir.cs"
Link="Common\Interop\Unix\Interop.MkDir.cs" />
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.Open.cs"
Link="Common\Interop\Unix\Interop.Open.cs" />
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.OpenFlags.cs"
Expand Down Expand Up @@ -166,7 +164,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="$(LibrariesProjectRoot)System.Security.AccessControl\src\System.Security.AccessControl.csproj" />
<ProjectReference Include="$(LibrariesProjectRoot)System.Security.Principal.Windows\src\System.Security.Principal.Windows.csproj" />
<ProjectReference Include="$(LibrariesProjectRoot)System.Security.Principal.Windows\src\System.Security.Principal.Windows.csproj" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.Collections" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -434,23 +434,6 @@ private SemaphoreSlim EnsureAsyncActiveSemaphoreInitialized()
return LazyInitializer.EnsureInitialized(ref _asyncActiveSemaphore, () => new SemaphoreSlim(1, 1));
}

private static void CreateDirectory(string directoryPath)
{
int result = Interop.Sys.MkDir(directoryPath, (int)Interop.Sys.Permissions.Mask);

// If successful created, we're done.
if (result >= 0)
return;

// If the directory already exists, consider it a success.
Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo();
if (errorInfo.Error == Interop.Error.EEXIST)
return;

// Otherwise, fail.
throw Interop.GetExceptionForIoErrno(errorInfo, directoryPath, isDirectory: true);
}

/// <summary>Creates an anonymous pipe.</summary>
/// <param name="reader">The resulting reader end of the pipe.</param>
/// <param name="writer">The resulting writer end of the pipe.</param>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -298,9 +298,8 @@ public static void CreateDirectory(string fullPath)
{
return; // Path already exists and it's a directory.
}
else if (errorInfo.Error == Interop.Error.ENOENT)
else if (errorInfo.Error == Interop.Error.ENOENT) // Some parts of the path don't exist yet.
{
// Some parts of the path don't exist yet.
CreateParentsAndDirectory(fullPath);
}
else
Expand All @@ -309,20 +308,19 @@ public static void CreateDirectory(string fullPath)
}
}

public static void CreateParentsAndDirectory(string fullPath)
private static void CreateParentsAndDirectory(string fullPath)
{
// Try create parents bottom to top and track those that could not
// be created due to missing parents. Then create them top to bottom.
List<string> stackDir = new List<string>();

stackDir.Add(fullPath);
using ValueListBuilder<int> stackDir = new(stackalloc int[32]); // 32 arbitrarily chosen
stackDir.Append(fullPath.Length);

int i = fullPath.Length - 1;
// Trim trailing separator.
if (PathInternal.IsDirectorySeparator(fullPath[i]))
{
i--;
i--; // Trim trailing separator.
}

do
{
// Find the end of the parent directory.
Expand All @@ -332,13 +330,11 @@ public static void CreateParentsAndDirectory(string fullPath)
i--;
}

// Try create it.
string mkdirPath = fullPath.Substring(0, i);
ReadOnlySpan<char> mkdirPath = fullPath.AsSpan(0, i);
int result = Interop.Sys.MkDir(mkdirPath, (int)Interop.Sys.Permissions.Mask);
if (result == 0)
{
// Created parent.
break;
break; // Created parent.
}

Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo();
Expand All @@ -348,7 +344,7 @@ public static void CreateParentsAndDirectory(string fullPath)
// We'll try to create its parent on the next iteration.

// Track this path for later creation.
stackDir.Add(mkdirPath);
stackDir.Append(mkdirPath.Length);
}
else if (errorInfo.Error == Interop.Error.EEXIST)
{
Expand All @@ -358,15 +354,15 @@ public static void CreateParentsAndDirectory(string fullPath)
}
else
{
throw Interop.GetExceptionForIoErrno(errorInfo, mkdirPath, isDirectory: true);
throw Interop.GetExceptionForIoErrno(errorInfo, mkdirPath.ToString(), isDirectory: true);
}
i--;
} while (i > 0);

// Create directories that had missing parents.
for (i = stackDir.Count - 1; i >= 0; i--)
for (i = stackDir.Length - 1; i >= 0; i--)
{
string mkdirPath = stackDir[i];
ReadOnlySpan<char> mkdirPath = fullPath.AsSpan(0, stackDir[i]);
int result = Interop.Sys.MkDir(mkdirPath, (int)Interop.Sys.Permissions.Mask);
if (result < 0)
{
Expand All @@ -386,7 +382,7 @@ public static void CreateParentsAndDirectory(string fullPath)
}
}

throw Interop.GetExceptionForIoErrno(errorInfo, mkdirPath, isDirectory: true);
throw Interop.GetExceptionForIoErrno(errorInfo, mkdirPath.ToString(), isDirectory: true);
}
}
}
Expand Down