Skip to content

Commit

Permalink
Reduce allocations for CreateDirectory (#61777)
Browse files Browse the repository at this point in the history
* introduce an overload that accepts ROS<char> instead of a string

* remove dead code

* avoid string allocations

* remove List<int> allocation

* Apply suggestions from code review

Co-authored-by: Stephen Toub <[email protected]>
  • Loading branch information
adamsitnik and stephentoub authored Nov 19, 2021
1 parent b4d16b7 commit ba4eae0
Show file tree
Hide file tree
Showing 4 changed files with 24 additions and 39 deletions.
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)]
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);

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

0 comments on commit ba4eae0

Please sign in to comment.