[netcore] Make Environment calls thread-safe (mono/mono#16852)
authorFilip Navara <navara@emclient.com>
Mon, 16 Sep 2019 21:16:14 +0000 (23:16 +0200)
committerZoltan Varga <vargaz@gmail.com>
Mon, 16 Sep 2019 21:16:14 +0000 (17:16 -0400)
* [netcore] Make Environment calls thread-safe

* Address PR feedback

* Address PR feedback

Commit migrated from https://github.com/mono/mono/commit/17fe4757a3c4a77bedd7e451ef092f49c6e3a57f

src/mono/mono/metadata/icall-def-netcore.h
src/mono/netcore/System.Private.CoreLib/src/System/Environment.Unix.cs

index 5abeb9d..0373349 100644 (file)
@@ -84,7 +84,6 @@ NOHANDLES(ICALL(ENV_1, "Exit", ves_icall_System_Environment_Exit))
 HANDLES(ENV_1a, "FailFast", ves_icall_System_Environment_FailFast, void, 3, (MonoString, MonoException, MonoString))
 HANDLES(ENV_2, "GetCommandLineArgs", ves_icall_System_Environment_GetCommandLineArgs, MonoArray, 0, ())
 HANDLES(ENV_3, "GetEnvironmentVariableNames", ves_icall_System_Environment_GetEnvironmentVariableNames, MonoArray, 0, ())
-HANDLES(ENV_8, "InternalSetEnvironmentVariable", ves_icall_System_Environment_InternalSetEnvironmentVariable, void, 4, (const_gunichar2_ptr, gint32, const_gunichar2_ptr, gint32))
 NOHANDLES(ICALL(ENV_9, "get_ExitCode", mono_environment_exitcode_get))
 NOHANDLES(ICALL(ENV_10, "get_HasShutdownStarted", ves_icall_System_Environment_get_HasShutdownStarted))
 HANDLES(ENV_11, "get_MachineName", ves_icall_System_Environment_get_MachineName, MonoString, 0, ())
index acbaab1..6b186fd 100644 (file)
@@ -3,7 +3,7 @@
 // See the LICENSE file in the project root for more information.
 
 using System.Collections;
-using System.Globalization;
+using System.Collections.Generic;
 using System.Diagnostics;
 using System.Runtime.CompilerServices;
 using System.Threading;
@@ -13,36 +13,91 @@ namespace System
 {
        partial class Environment
        {
+               private static Dictionary<string, string> s_environment;
+
                static string GetEnvironmentVariableCore (string variable)
                {
-                       using (var h = RuntimeMarshal.MarshalString (variable)) {
-                               return internalGetEnvironmentVariable_native (h.Value);
+                       Debug.Assert(variable != null);
+
+                       if (s_environment == null) {
+                               using (var h = RuntimeMarshal.MarshalString (variable)) {
+                                       return internalGetEnvironmentVariable_native (h.Value);
+                               }
+                       }
+
+                       variable = TrimStringOnFirstZero (variable);
+                       lock (s_environment) {
+                               s_environment.TryGetValue (variable, out string value);
+                               return value;
                        }
                }
 
-               [MethodImplAttribute (MethodImplOptions.InternalCall)]
-               extern static string internalGetEnvironmentVariable_native (IntPtr variable);
+               static unsafe void SetEnvironmentVariableCore (string variable, string? value)
+               {
+                       Debug.Assert(variable != null);
 
-               [MethodImplAttribute (MethodImplOptions.InternalCall)]
-               private extern static string [] GetEnvironmentVariableNames ();
+                       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 ()
                {
-                       Hashtable vars = new Hashtable ();
-                       foreach (string name in GetEnvironmentVariableNames ()) {
-                               vars [name] = GetEnvironmentVariableCore (name);
+                       var results = new Hashtable();
+
+                       EnsureEnvironmentCached();
+                       lock (s_environment) {
+                               foreach (var keyValuePair in s_environment) {
+                                       results.Add(keyValuePair.Key, keyValuePair.Value);
+                               }
                        }
-                       return vars;
+
+                       return results;
                }
 
-               static unsafe void SetEnvironmentVariableCore (string variable, string value)
+               private static string TrimStringOnFirstZero (string value)
                {
-                       fixed (char *fixed_variable = variable)
-                       fixed (char *fixed_value = value)
-                               InternalSetEnvironmentVariable (fixed_variable, variable.Length, fixed_value, value?.Length ?? 0);
+                       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>();
+
+                       foreach (string name in GetEnvironmentVariableNames ()) {
+                               if (name != null) {
+                                       using (var h = RuntimeMarshal.MarshalString (name)) {
+                                               results.Add (name, internalGetEnvironmentVariable_native (h.Value));
+                                       }
+                               }
+                       }
+
+                       return results;
+               }
+
+
                [MethodImplAttribute (MethodImplOptions.InternalCall)]
-               static extern unsafe void InternalSetEnvironmentVariable (char *variable, int variable_length, char *value, int value_length);
+               extern static string internalGetEnvironmentVariable_native (IntPtr variable);
+
+               [MethodImplAttribute (MethodImplOptions.InternalCall)]
+               private extern static string [] GetEnvironmentVariableNames ();
        }
 }