Skip to content

Commit

Permalink
De-duplicate Unix Environment variables between Mono and native AOT (#…
Browse files Browse the repository at this point in the history
…76853)

* De-duplicate Unix Environment variables between Mono and native AOT

CoreCLR left on Win32 PAL

* Delete unused file
  • Loading branch information
jkotas authored Oct 12, 2022
1 parent f2de14d commit 6ee57c0
Show file tree
Hide file tree
Showing 8 changed files with 12 additions and 196 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,6 @@
<Link>Interop\Windows\OleAut32\Interop.VariantClear.cs</Link>
</Compile>
<Compile Include="System\Environment.NativeAot.Windows.cs" />
<Compile Include="System\Environment.NativeAot.Win32.cs" />
<Compile Include="$(CommonPath)\Interop\Windows\Kernel32\Interop.GetTickCount64.cs">
<Link>Interop\Windows\Kernel32\Interop.GetTickCount64.cs</Link>
</Compile>
Expand Down Expand Up @@ -325,18 +324,12 @@
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.Exit.cs">
<Link>Interop\Unix\System.Native\Interop.Exit.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.GetEnv.cs">
<Link>Interop\Unix\System.Native\Interop.GetEnv.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.SchedGetCpu.cs">
<Link>Interop\Unix\System.Native\Interop.SchedGetCpu.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.Threading.cs">
<Link>Interop\Unix\System.Native\Interop.Threading.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.GetEnviron.cs">
<Link>Interop\Unix\System.Native\Interop.GetEnviron.cs</Link>
</Compile>
</ItemGroup>
<ItemGroup>
<Compile Include="$(CompilerCommonPath)\System\Collections\Generic\ArrayBuilder.cs">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using System.Runtime;
using System.Text;
using System.Threading;

namespace System
Expand All @@ -16,157 +11,9 @@ public static partial class Environment
{
internal static int CurrentNativeThreadId => ManagedThreadId.Current;

private static Dictionary<string, string> s_environment;

private static string? GetEnvironmentVariableCore(string variable)
{
Debug.Assert(variable != null);

if (s_environment == null)
{
return Marshal.PtrToStringUTF8(Interop.Sys.GetEnv(variable));
}

lock (s_environment)
{
variable = TrimStringOnFirstZero(variable);
s_environment.TryGetValue(variable, out string? value);
return value;
}
}

private static void SetEnvironmentVariableCore(string variable, string? value)
{
Debug.Assert(variable != null);

EnsureEnvironmentCached();
lock (s_environment)
{
variable = TrimStringOnFirstZero(variable);
value = value == null ? null : TrimStringOnFirstZero(value);
if (string.IsNullOrEmpty(value))
{
s_environment.Remove(variable);
}
else
{
s_environment[variable] = value;
}
}
}

public static IDictionary GetEnvironmentVariables()
{
var results = new Hashtable();

EnsureEnvironmentCached();
lock (s_environment)
{
foreach (var keyValuePair in s_environment)
{
results.Add(keyValuePair.Key, keyValuePair.Value);
}
}

return results;
}

private static string TrimStringOnFirstZero(string value)
{
int index = value.IndexOf('\0');
if (index >= 0)
{
return value.Substring(0, index);
}
return value;
}

private static void EnsureEnvironmentCached()
{
if (s_environment == null)
{
Interlocked.CompareExchange(ref s_environment, GetSystemEnvironmentVariables(), null);
}
}

private static Dictionary<string, string> GetSystemEnvironmentVariables()
{
var results = new Dictionary<string, string>();

IntPtr block = Interop.Sys.GetEnviron();
if (block != IntPtr.Zero)
{
try
{
IntPtr blockIterator = block;

// Per man page, environment variables come back as an array of pointers to strings
// Parse each pointer of strings individually
while (ParseEntry(blockIterator, out string? key, out string? value))
{
if (key != null && value != null)
{
try
{
// Add may throw if the environment block was corrupted leading to duplicate entries.
// We allow such throws and eat them (rather than proactively checking for duplication)
// to provide a non-fatal notification about the corruption.
results.Add(key, value);
}
catch (ArgumentException) { }
}

// Increment to next environment variable entry
blockIterator += IntPtr.Size;
}
}
finally
{
Interop.Sys.FreeEnviron(block);
}
}

return results;

// Use a local, unsafe function since we cannot use `yield return` inside of an `unsafe` block
static unsafe bool ParseEntry(IntPtr current, out string? key, out string? value)
{
// Setup
key = null;
value = null;

// Point to current entry
byte* entry = *(byte**)current;

// Per man page, "The last pointer in this array has the value NULL"
// Therefore, if entry is null then we're at the end and can bail
if (entry == null)
return false;

// Parse each byte of the entry until we hit either the separator '=' or '\0'.
// This finds the split point for creating key/value strings below.
// On some old OS, the environment block can be corrupted.
// Some will not have '=', so we need to check for '\0'.
byte* splitpoint = entry;
while (*splitpoint != '=' && *splitpoint != '\0')
splitpoint++;

// Skip over entries starting with '=' and entries with no value (just a null-terminating char '\0')
if (splitpoint == entry || *splitpoint == '\0')
return true;

// The key is the bytes from start (0) until our splitpoint
key = new string((sbyte*)entry, 0, checked((int)(splitpoint - entry)));
// The value is the rest of the bytes starting after the splitpoint
value = new string((sbyte*)(splitpoint + 1));

return true;
}
}
public static long TickCount64 => (long)RuntimeImports.RhpGetTickCount64();

[DoesNotReturn]
private static void ExitRaw() => Interop.Sys.Exit(s_latchedExitCode);

public static long TickCount64 => (long)RuntimeImports.RhpGetTickCount64();
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Buffers;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Diagnostics.CodeAnalysis;

namespace System
{
Expand All @@ -12,5 +10,8 @@ public static partial class Environment
internal static int CurrentNativeThreadId => unchecked((int)Interop.Kernel32.GetCurrentThreadId());

public static long TickCount64 => (long)Interop.Kernel32.GetTickCount64();

[DoesNotReturn]
private static void ExitRaw() => Interop.Kernel32.ExitProcess(s_latchedExitCode);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2049,6 +2049,12 @@
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.GetDefaultTimeZone.AnyMobile.cs" Condition="'$(TargetsAndroid)' == 'true' or '$(TargetsLinuxBionic)' == 'true' or '$(IsiOSLike)' == 'true'">
<Link>Common\Interop\Unix\System.Native\Interop.GetDefaultTimeZone.AnyMobile.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.GetEnv.cs">
<Link>Common\Interop\Unix\System.Native\Interop.GetEnv.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.GetEnviron.cs">
<Link>Common\Interop\Unix\System.Native\Interop.GetEnviron.cs</Link>
</Compile>
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.GetHostName.cs">
<Link>Common\Interop\Unix\System.Native\Interop.GetHostName.cs</Link>
</Compile>
Expand Down Expand Up @@ -2205,6 +2211,7 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Environment.OSVersion.OSX.cs" Condition="'$(IsOSXLike)' == 'true' AND '$(TargetsMacCatalyst)' != 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)System\Environment.OSVersion.MacCatalyst.cs" Condition="'$(TargetsMacCatalyst)' == 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)System\Environment.GetFolderPathCore.Unix.cs" Condition="'$(IsiOSLike)' != 'true' and '$(TargetsAndroid)' != 'true' and '$(TargetsLinuxBionic)' != 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)System\Environment.Variables.Unix.cs" Condition="'$(FeatureCoreCLR)' != 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CalendarData.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CultureData.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CultureInfo.Unix.cs" />
Expand Down
7 changes: 0 additions & 7 deletions src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -279,14 +279,7 @@
<Compile Include="$(BclSourcesRoot)\System\Threading\ThreadPool.Mono.cs" />
</ItemGroup>
<ItemGroup Condition="'$(TargetsUnix)' == 'true' or '$(TargetsBrowser)' == 'true'">
<Compile Include="$(BclSourcesRoot)\System\Environment.Unix.Mono.cs" />
<Compile Include="$(BclSourcesRoot)\System\Threading\LowLevelLifoSemaphore.Unix.Mono.cs" />
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.GetEnv.cs">
<Link>Common\Interop\Unix\System.Native\Interop.GetEnv.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.GetEnviron.cs">
<Link>Common\Interop\Unix\System.Native\Interop.GetEnviron.cs</Link>
</Compile>
</ItemGroup>
<ItemGroup Condition="'$(TargetsBrowser)' == 'true'">
<Compile Include="$(BclSourcesRoot)\System\Threading\TimerQueue.Browser.Mono.cs" />
Expand Down

0 comments on commit 6ee57c0

Please sign in to comment.