Merge pull request #3506 from ntherning/make-ZoneTest-succeed-on-mono-on-windows
authorMarek Safar <marek.safar@gmail.com>
Fri, 2 Sep 2016 13:01:32 +0000 (15:01 +0200)
committerGitHub <noreply@github.com>
Fri, 2 Sep 2016 13:01:32 +0000 (15:01 +0200)
Make Zone.CreateFromUrl() work with URLs like file://C:\foo\bar

26 files changed:
man/Makefile.am
man/cert-sync.1 [new file with mode: 0644]
man/mkbundle.1
mcs/class/Mono.Data.Sqlite/Mono.Data.Sqlite_2.0/SQLite3.cs
mcs/class/Mono.Data.Sqlite/Mono.Data.Sqlite_2.0/SQLiteBase.cs
mcs/class/Mono.Data.Sqlite/Mono.Data.Sqlite_2.0/UnsafeNativeMethods.cs
mcs/class/corlib/System/AppDomainSetup.cs
mcs/class/corlib/Test/System.Reflection/PropertyInfoTest.cs
mcs/class/corlib/Test/System.Runtime.InteropServices/MarshalTest.cs
mcs/class/corlib/Test/System/AppDomainSetupTest.cs
mcs/class/dlr/Runtime/Microsoft.Scripting.Core/Actions/DynamicObject.cs
mcs/tests/dtest-065.cs [new file with mode: 0644]
mcs/tests/ver-il-net_4_x.xml
mcs/tools/mkbundle/mkbundle.cs
mono/metadata/object-internals.h
mono/metadata/object.c
mono/metadata/threads-types.h
mono/metadata/threads.c
mono/mini/main.c
mono/mini/mini-exceptions.c
mono/tests/Makefile.am
mono/tests/abort-cctor.cs [new file with mode: 0644]
mono/utils/mono-threads-posix.c
mono/utils/mono-threads-windows.c
mono/utils/mono-threads.c
mono/utils/mono-threads.h

index de2b6ac7c3ff22a480831f8a10292f0846749835..0c8b29892ff5f582e121af6d36c300073815cdb5 100644 (file)
@@ -2,6 +2,7 @@ man_MANS = \
        al.1                  \
        cert2spc.1            \
        certmgr.1             \
+       cert-sync.1           \
        chktrust.1            \
        cilc.1                \
        crlupdate.1           \
diff --git a/man/cert-sync.1 b/man/cert-sync.1
new file mode 100644 (file)
index 0000000..66d3d60
--- /dev/null
@@ -0,0 +1,52 @@
+.\" 
+.\" cert-sync  manual page.
+.\" Copyright 2016 Microsoft Corp
+.\" Author:
+.\"   Jo Shields <joshield@microsoft.com>
+.\"
+.TH Mono "cert-sync"
+.SH NAME
+cert-sync \- Mono Certificate Store Sync Tool
+.SH SYNOPSIS
+.PP
+.B cert-sync [--quiet] [--user] filename
+.SH DESCRIPTION
+This tool allows you to populate a Mono certificate store, from a large 
+concatenated list of certificates in PEM format (commonly provided on most 
+Linux distributions).
+
+Its use is intended to be automated at Mono install time, by distribution 
+packagers, to seamlessly provide SSL support to Mono applications without 
+further user interaction.
+.SH OPTIONS
+.TP
+.I "--quiet"
+Suppress verbose output
+.TP
+.I "--user"
+Populate the per-user store in the user's home directory, instead of the 
+system-wide store.
+.TP
+.I "filename.crt"
+Path to a certificate bundle. The Mono store will have any extra entries 
+removed, and new entries added, to reflect the provided file.
+
+.SH EXAMPLES
+.TP
+.B cert-sync /etc/ssl/certs/ca-certificates.crt
+Synchronize the machine store, from the Debian cert store location
+.TP
+.B cert-sync --user /etc/pki/tls/certs/ca-bundle.crt
+Synchronize the user store, from the Red Hat cert store location
+
+.SH AUTHOR
+Written by Jo Shields
+
+.SH COPYRIGHT
+Copyright (C) 2016 Microsoft Corp
+.SH MAILING LISTS
+Visit http://lists.ximian.com/mailman/listinfo/mono-list for details.
+.SH WEB SITE
+Visit http://www.mono-project.com for details
+.SH SEE ALSO
+.BR certmgr(1)
index 160786d94f6a2643fba63837e3f12cdc5826658b..95861e0aeab8e58ebbaaf64c8758ffe813b0f818 100644 (file)
@@ -40,6 +40,16 @@ command:
        $ mkbundle -o hello --simple hello.exe
 
 .fi
+.PP
+You can configure options to be passed to the Mono runtime directly
+into your executable, for this, use the 
+.I --options
+flag.  For example, the following disables inlining, by passing the
+"-O=-inline" command line option to the embedded executable:
+.nf
+
+       $ mkbundle -o hello --options -O=-inline --simple hello.exe
+
 .PP
 The simple version allows for cross-compiling, this requires a Mono
 runtime to be installed in the ~/.mono/targets/TARGET/mono to be
@@ -119,6 +129,12 @@ This option will bundle all of the referenced assemblies for the
 assemblies listed on the command line option.  This is useful to
 distribute a self-contained image.
 .TP
+.I "--env KEY=VALUE"
+Use this to hardcode an environment variable at runtime for KEY to be
+mapped to VALUE.   This is useful in scenarios where you want to
+enable certain Mono runtime configuration options that are controlled
+by environment variables.
+.TP
 .I "--fetch-target target"
 Downloads a precompiled runtime for the specified target from the Mono
 distribution site.
@@ -167,6 +183,22 @@ image created.
 Places the output on `out'.  If the flag -c is specified, this is the
 C host program.  If not, this contains the resulting executable.
 .TP
+.I "--options OPTS"
+Since the resulting executable will be treated as a standalone
+program, you can use this option to pass configuration options to the
+Mono runtime and bake those into the resulting executable.  These
+options are specified as 
+.I OPTS.
+.Sp
+You can use the above to configure options that you would typically
+pass on the command line to Mono, before the main program is
+executed.   
+.Sp
+Additionally, users of your binary can still configure their own
+options by setting the 
+.I MONO_ENV_OPTIONS
+environment variable.   
+.TP
 .I "--target-server SERVER"
 By default the mkbundle tool will download from a Mono server the
 target runtimes, you can specify a different server to provide
index 6f757238cd4139809efe4ca4230369ff3344c0bc..34556edb930a21476dd4240285fe9865ea428db6 100644 (file)
@@ -125,9 +125,9 @@ namespace Mono.Data.Sqlite
        // Compatibility with versions < 3.5.0\r
         int n;\r
 \r
-       try {\r
+       if (UnsafeNativeMethods.use_sqlite3_open_v2) {\r
                n = UnsafeNativeMethods.sqlite3_open_v2(ToUTF8(strFilename), out db, (int)flags, IntPtr.Zero);\r
-       } catch (EntryPointNotFoundException) {\r
+       } else {\r
                Console.WriteLine ("Your sqlite3 version is old - please upgrade to at least v3.5.0!");\r
                n = UnsafeNativeMethods.sqlite3_open (ToUTF8 (strFilename), out db);\r
        }\r
index df8a5d787ab0daefe2ea5714dc621b1342f9f8b2..eca1bddfc6b406b6159fccd06ae84ca69a2f6486 100644 (file)
@@ -208,9 +208,9 @@ namespace Mono.Data.Sqlite
 #else\r
         ResetConnection(db);\r
         int n;\r
-        try {\r
+        if (UnsafeNativeMethods.use_sqlite3_close_v2) {\r
           n = UnsafeNativeMethods.sqlite3_close_v2(db);\r
-        } catch (EntryPointNotFoundException) {\r
+        } else {\r
           n = UnsafeNativeMethods.sqlite3_close(db);\r
         }\r
 #endif\r
index ba16a1763ff92b3141d7515bf8f463affd6c2ab4..bc9826d29f47dc83a2ad0875d14c046ae60b7343 100644 (file)
@@ -16,6 +16,27 @@ namespace Mono.Data.Sqlite
 #endif\r
   internal static class UnsafeNativeMethods\r
   {\r
+    internal static readonly bool use_sqlite3_close_v2 = false;\r
+    internal static readonly bool use_sqlite3_open_v2 = false;\r
+    internal static readonly bool use_sqlite3_create_function_v2 = false;\r
+    static UnsafeNativeMethods()\r
+    {\r
+      // calculate the version number parts\r
+      // https://www.sqlite.org/c3ref/c_source_id.html\r
+      // (<major> * 1000000) + (<minor> * 1000) + (<release>)\r
+      int versionNumber = sqlite3_libversion_number();\r
+      int release = versionNumber % 1000;\r
+      int minor = (versionNumber / 1000) % 1000;\r
+      int major = versionNumber / 1000000;\r
+      Version version = new Version(major, minor, release);\r
+\r
+      // set the various versions\r
+      // https://sqlite.org/changes.html\r
+      use_sqlite3_open_v2 = version >= new Version(3, 5, 0);\r
+      use_sqlite3_close_v2 = version >= new Version(3, 7, 14);\r
+      use_sqlite3_create_function_v2 = version >= new Version(3, 7, 3);\r
+    }\r
+\r
 #if !SQLITE_STANDARD\r
 \r
 #if !USE_INTEROP_DLL\r
@@ -730,6 +751,13 @@ namespace Mono.Data.Sqlite
 #endif\r
     internal static extern int sqlite3_free (IntPtr ptr);\r
 \r
+#if !PLATFORM_COMPACTFRAMEWORK\r
+    [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]\r
+#else\r
+    [DllImport(SQLITE_DLL)]\r
+#endif\r
+    internal static extern int sqlite3_libversion_number();\r
+\r
     #endregion\r
   }\r
 \r
index c1865f34bd366939da81af189dc94ab10a203ce4..309535cfdd7e4ba32f2f55e8d86bdf6ab733a71e 100644 (file)
@@ -36,6 +36,7 @@ using System.IO;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using System.Security;
+using System.Security.Permissions;
 using System.Runtime.Serialization.Formatters.Binary;
 
 using System.Runtime.Hosting;
@@ -139,14 +140,19 @@ namespace System
                                appBase = appBase.Substring (7);
                                if (Path.DirectorySeparatorChar != '/')
                                        appBase = appBase.Replace ('/', Path.DirectorySeparatorChar);
-                               if (Environment.IsRunningOnWindows) {
-                                       // Under Windows prepend "//" to indicate it's a local file
-                                       appBase = "//" + appBase;
+                       }
+                       appBase = Path.GetFullPath (appBase);
+
+                       if (Path.DirectorySeparatorChar != '/') {
+                               bool isExtendedPath = appBase.StartsWith (@"\\?\", StringComparison.Ordinal);
+                               if (appBase.IndexOf (':', isExtendedPath ? 6 : 2) != -1) {
+                                       throw new NotSupportedException ("The given path's format is not supported.");
                                }
-                       } else {
-                               appBase = Path.GetFullPath (appBase);
                        }
 
+                       // Trigger a validation of the path
+                       new FileIOPermission (FileIOPermissionAccess.PathDiscovery, appBase);
+
                        return appBase;
                }
 
index 2fe870111ca33ce341b403cb3b48534ec9033fdf..4c4388ec8b3e17bb2eef08f784b9d42e2f934610 100644 (file)
@@ -391,7 +391,12 @@ namespace MonoTests.System.Reflection
                        var p = t.GetProperty ("Prop");
                        Assert.AreEqual ("test", p.GetConstantValue (), "#1");
 
-                       File.Delete (Path.Combine (Path.GetTempPath (), an));
+                       try {
+                               // This throws an exception under MS.NET and Mono on Windows,
+                               // open files cannot be deleted. That's fine.
+                               File.Delete (Path.Combine (Path.GetTempPath (), an));
+                       } catch (Exception) {
+                       }
 
                        var pa = typeof (TestE).GetProperty ("PropE");
                        try {
index 094fb8a54811c8396c25b5bb393dac3ce62d5e42..d6254137ee0122b4b5008f679181c54239ccc207 100644 (file)
@@ -208,8 +208,8 @@ namespace MonoTests.System.Runtime.InteropServices
                [Test]
                public void GetHINSTANCE ()
                {
-                       if (RunningOnUnix)
-                               Assert.Ignore ("GetHINSTANCE only applies to Windows.");
+                       if (RunningOnMono)
+                               Assert.Ignore ("GetHINSTANCE only applies to .NET on Windows.");
 
                        Assembly a;
                        IntPtr hinstance;
@@ -803,10 +803,9 @@ namespace MonoTests.System.Runtime.InteropServices
                        ex = Marshal.GetExceptionForHR (E_INVALIDARG);
                        Assert.AreEqual (typeof (ArgumentException), ex.GetType (), "E_INVALIDARG");
                }
-               bool RunningOnUnix {
+               bool RunningOnMono {
                        get {
-                               int p = (int) Environment.OSVersion.Platform;
-                               return ((p == 4) || (p == 128) || (p == 6));
+                               return (Type.GetType ("System.MonoType", false) != null);
                        }
                }
 
index 121d1c45f10a88ceae0f80fdbbfe3575d7f7cb70..387d0318a3f45cc6eb627e3241dd6ad2bd40704a 100644 (file)
@@ -23,8 +23,12 @@ namespace MonoTests.System
 
                private bool RunningOnWindows {
                        get {
-                               int os = (int)Environment.OSVersion.Platform;
-                               return (os != 4);
+                               return Path.DirectorySeparatorChar == '\\';
+                       }
+               }
+               private bool RunningOnMono {
+                       get {
+                               return (Type.GetType ("System.MonoType", false) != null);
                        }
                }
 
@@ -72,22 +76,18 @@ namespace MonoTests.System
                [Test]
                public void ApplicationBase1 ()
                {
-                       string expected_path = tmpPath.Replace(@"\", @"/");
+                       string expected_path = tmpPath;
                        AppDomainSetup setup = new AppDomainSetup ();
-                       string fileUri = "file://" + expected_path;
+                       string fileUri = "file://" + tmpPath.Replace(@"\", @"/");
                        setup.ApplicationBase = fileUri;
-                       // with MS 1.1 SP1 the expected_path starts with "//" but this make
-                       // sense only under Windows (i.e. reversed \\ for local files)
-                       if (RunningOnWindows)
-                               expected_path = "//" + expected_path;
                        try {
-                               // under 2.0 the NotSupportedException is throw when getting 
+                               // under .NET the NotSupportedException is throw when getting 
                                // (and not setting) the ApplicationBase property
                                Assert.AreEqual (expected_path, setup.ApplicationBase);
                        }
                        catch (NotSupportedException) {
-                               // however the path is invalid only on Windows
-                               if (!RunningOnWindows)
+                               // however the path is invalid only on .NET
+                               if (RunningOnMono)
                                        throw;
                        }
                }
@@ -114,16 +114,17 @@ namespace MonoTests.System
                {
                        AppDomainSetup setup = new AppDomainSetup ();
                        setup.ApplicationBase = "lala:la";
-                       try {
-                               // under 2.0 the NotSupportedException is throw when getting 
-                               // (and not setting) the ApplicationBase property
+                       if (!RunningOnWindows) {
                                Assert.AreEqual (Path.GetFullPath ("lala:la"), setup.ApplicationBase);
-                       }
-                       catch (NotSupportedException) {
-                               // however the path is invalid only on Windows
-                               // (same exceptions as Path.GetFullPath)
-                               if (!RunningOnWindows)
-                                       throw;
+                       } else {
+                               // On Windows we expect a NotSupportedException to be thrown because
+                               // of the illegal character (:) in the path
+                               try {
+                                       Assert.Fail ("NotSupportedException expected but setup.ApplicationBase returned:" + setup.ApplicationBase);
+                               }
+                               catch (NotSupportedException) {
+                                       // Expected
+                               }
                        }
                }
 
@@ -133,16 +134,18 @@ namespace MonoTests.System
                        // This is failing because of (probably) a windows-ism, so don't worry
                        AppDomainSetup setup = new AppDomainSetup ();
                        setup.ApplicationBase = "file:///lala:la";
-                       try {
-                               // under 2.0 the NotSupportedException is throw when getting 
-                               // (and not setting) the ApplicationBase property
-                               Assert.AreEqual ("/lala:la", setup.ApplicationBase);
-                       }
-                       catch (NotSupportedException) {
-                               // however the path is invalid only on Windows
-                               // (same exceptions as Path.GetFullPath)
-                               if (!RunningOnWindows)
-                                       throw;
+                       string expected = "/lala:la";
+                       if (!RunningOnWindows) {
+                               Assert.AreEqual (expected, setup.ApplicationBase);
+                       } else {
+                               // On Windows we expect a NotSupportedException to be thrown because
+                               // of the illegal character (:) in the path
+                               try {
+                                       Assert.Fail ("NotSupportedException expected but setup.ApplicationBase returned:" + setup.ApplicationBase);
+                               }
+                               catch (NotSupportedException) {
+                                       // Expected
+                               }
                        }
                }
 
@@ -153,19 +156,45 @@ namespace MonoTests.System
                        setup.ApplicationBase = "la?lala";
                        // paths containing "?" are *always* bad on Windows
                        // but are legal for linux so we return a full path
-                       if (RunningOnWindows) {
+                       if (!RunningOnWindows) {
+                               Assert.AreEqual (Path.GetFullPath ("la?lala"), setup.ApplicationBase);
+                       } else {
+                               // On Windows we expect a ArgumentException to be thrown because
+                               // of the illegal character (?) in the path
                                try {
-                                       // ArgumentException is throw when getting 
-                                       // (and not setting) the ApplicationBase property
-                                       Assert.Fail ("setup.ApplicationBase returned :" + setup.ApplicationBase);
+                                       Assert.Fail ("ArgumentException expected but setup.ApplicationBase returned:" + setup.ApplicationBase);
                                }
                                catch (ArgumentException) {
+                                       // Expected
+                               }
+                       }
+               }
+
+               [Test]
+               public void ApplicationBase7 ()
+               {
+                       if (RunningOnWindows) {
+                               // Extended paths are Windows only
+                               AppDomainSetup setup = new AppDomainSetup ();
+                               string expected = @"\\?\" + curDir;
+                               setup.ApplicationBase = expected;
+                               Assert.AreEqual (expected, setup.ApplicationBase);
+                       }
+               }
+
+               [Test]
+               public void ApplicationBase8 ()
+               {
+                       if (RunningOnWindows) {
+                               // Extended paths are Windows only
+                               AppDomainSetup setup = new AppDomainSetup ();
+                               setup.ApplicationBase = @"\\?\C:\lala:la";
+                               try {
+                                       Assert.Fail ("NotSupportedException expected but setup.ApplicationBase returned:" + setup.ApplicationBase);
                                }
-                               catch (Exception e) {
-                                       Assert.Fail ("Unexpected exception: " + e.ToString ());
+                               catch (NotSupportedException) {
+                                       // Expected
                                }
-                       } else {
-                               Assert.AreEqual (Path.GetFullPath ("la?lala"), setup.ApplicationBase);
                        }
                }
 
index 71341f5bc2dc3d83de248228b9d237b2d0d70b16..7da7303d755466e2c3bcdb87b7800fe0e914594a 100644 (file)
@@ -501,6 +501,44 @@ namespace System.Dynamic {
                         binder.ReturnType
                     );
 
+#if MONO // referencesource version
+                    Expression condition;
+                    // If the return type can not be assigned null then just check for type assignablity otherwise allow null.
+                    if (binder.ReturnType.IsValueType && Nullable.GetUnderlyingType(binder.ReturnType) == null) {
+                        condition = Expression.TypeIs(resultMO.Expression, binder.ReturnType);
+                    }
+                    else {
+                        condition = Expression.OrElse(
+                                        Expression.Equal(resultMO.Expression, Expression.Constant(null)),
+                                        Expression.TypeIs(resultMO.Expression, binder.ReturnType));
+                    }
+
+                    var checkedConvert = Expression.Condition(
+                        condition,
+                        convert,
+                        Expression.Throw(
+                            Expression.New(typeof(InvalidCastException).GetConstructor(new Type[]{typeof(string)}),
+                                Expression.Call(
+                                    typeof(string).GetMethod("Format", new Type[] {typeof(string), typeof(object[])}),
+                                    Expression.Constant(convertFailed),
+                                    Expression.NewArrayInit(typeof(object),
+                                        Expression.Condition(
+                                            Expression.Equal(resultMO.Expression, Expression.Constant(null)),
+                                            Expression.Constant("null"),
+                                            Expression.Call(
+                                                resultMO.Expression,
+                                                typeof(object).GetMethod("GetType")
+                                            ),
+                                            typeof(object)
+                                        )
+                                    )
+                                )
+                            ),
+                            binder.ReturnType
+                        ),
+                        binder.ReturnType
+                    );
+#else
                     var checkedConvert = Expression.Condition(
                         Expression.TypeIs(resultMO.Expression, binder.ReturnType),
                         convert,
@@ -524,6 +562,7 @@ namespace System.Dynamic {
                         ),
                         binder.ReturnType
                     );
+#endif
 
                     resultMO = new DynamicMetaObject(checkedConvert, resultMO.Restrictions);
                 }
diff --git a/mcs/tests/dtest-065.cs b/mcs/tests/dtest-065.cs
new file mode 100644 (file)
index 0000000..fb51a71
--- /dev/null
@@ -0,0 +1,28 @@
+using System.Dynamic;
+
+public class TestConvert : DynamicObject
+{
+       public override bool TryConvert (ConvertBinder binder, out object result)
+       {
+               result = null;
+               return true;
+       }
+}
+
+public class Test : DynamicObject
+{
+       public override bool TryInvokeMember (InvokeMemberBinder binder, object [] args, out object result)
+       {
+               result = new TestConvert ();
+               return true;
+       }
+}
+
+public class XX
+{
+       public static void Main ()
+       {
+               dynamic t = new Test ();
+               string result = t.SomeMethod ();
+       }
+}
\ No newline at end of file
index cde4e9e70d68ea66bba24d1169045aa8b6be1057..a1c69e9de31fd90449ba623e6e2b94b2d3e49e44 100644 (file)
       </method>
     </type>
   </test>
+  <test name="dtest-065.cs">
+    <type name="TestConvert">
+      <method name="Boolean TryConvert(System.Dynamic.ConvertBinder, System.Object ByRef)" attrs="198">
+        <size>13</size>
+      </method>
+      <method name="Void .ctor()" attrs="6278">
+        <size>7</size>
+      </method>
+    </type>
+    <type name="Test">
+      <method name="Boolean TryInvokeMember(System.Dynamic.InvokeMemberBinder, System.Object[], System.Object ByRef)" attrs="198">
+        <size>17</size>
+      </method>
+      <method name="Void .ctor()" attrs="6278">
+        <size>7</size>
+      </method>
+    </type>
+    <type name="XX">
+      <method name="Void Main()" attrs="150">
+        <size>154</size>
+      </method>
+      <method name="Void .ctor()" attrs="6278">
+        <size>7</size>
+      </method>
+    </type>
+  </test>
   <test name="dtest-anontype-01.cs">
     <type name="C">
       <method name="Void Main()" attrs="150">
index b6244881f61d564bb9115a578ab81bd474486f95..702d74d16a7dd43ecab4b9be6f71f9017fe6f755 100755 (executable)
@@ -52,6 +52,7 @@ class MakeBundle {
        static bool custom_mode = true;
        static string embedded_options = null;
        static string runtime = null;
+       static Dictionary<string,string> environment = new Dictionary<string,string>();
        static string [] i18n = new string [] {
                "West",
                ""
@@ -281,6 +282,19 @@ class MakeBundle {
                        case "--quiet":
                                quiet = true;
                                break;
+                       case "-e":
+                       case "--env":
+                               if (i+1 == top) {
+                                       Help ();
+                                       return 1;
+                               }
+                               var env = args [++i];
+                               var p = env.IndexOf ('=');
+                               if (p == -1)
+                                       environment.Add (env, "");
+                               else
+                                       environment.Add (env.Substring (0, p), env.Substring (p+1));
+                               break;
                        default:
                                sources.Add (args [i]);
                                break;
@@ -477,6 +491,26 @@ class MakeBundle {
                        package.Position = package.Position + (align - (package.Position % align));
                }
 
+               public void AddStringPair (string entry, string key, string value)
+               {
+                       var kbytes = Encoding.UTF8.GetBytes (key);
+                       var vbytes = Encoding.UTF8.GetBytes (value);
+
+                       Console.WriteLine ("ADDING {0} to {1}", key, value);
+                       if (kbytes.Length > 255){
+                               Console.WriteLine ("The key value can not exceed 255 characters: " + key);
+                               Environment.Exit (1);
+                       }
+                               
+                       locations [entry] = Tuple.Create (package.Position, kbytes.Length+vbytes.Length+3);
+                       package.WriteByte ((byte)kbytes.Length);
+                       package.Write (kbytes, 0, kbytes.Length);
+                       package.WriteByte (0);
+                       package.Write (vbytes, 0, vbytes.Length);
+                       package.WriteByte (0);
+                       package.Position = package.Position + (align - (package.Position % align));
+               }
+
                public void Dump ()
                {
                        if (quiet)
@@ -564,6 +598,10 @@ class MakeBundle {
                        maker.Add ("config_dir:", config_dir);
                if (embedded_options != null)
                        maker.AddString ("options:", embedded_options);
+               if (environment.Count > 0){
+                       foreach (var key in environment.Keys)
+                               maker.AddStringPair ("env:" + key, key, environment [key]);
+               }
                maker.Dump ();
                maker.Close ();
                return true;
@@ -1080,6 +1118,7 @@ void          mono_register_config_for_assembly (const char* assembly_name, cons
                                   "    --options OPTIONS   Embed the specified Mono command line options on target\n" +
                                   "    --runtime RUNTIME   Manually specifies the Mono runtime to use\n" +
                                   "    --target-server URL Specified a server to download targets from, default is " + target_server + "\n" +
+                                  "    --env KEY=VALUE     Hardcodes an environment variable for the target\n" +
                                   "\n" +
                                   "--custom   Builds a custom launcher, options for --custom\n" +
                                   "    -c                  Produce stub only, do not compile\n" +
index a28e3d63fd99148d6aa19de0075ac55e615b59ab..03b02c7bc1b3abc41ca1d5f88e31d5530d6e01f0 100644 (file)
@@ -387,7 +387,7 @@ struct _MonoInternalThread {
         * same field there.
         */
        gsize start_notify_refcount;
-       gpointer unused2;
+       gsize abort_protected_block_count;
 };
 
 struct _MonoThread {
@@ -588,6 +588,7 @@ typedef struct {
        void (*mono_raise_exception_with_ctx) (MonoException *ex, MonoContext *ctx);
        gboolean (*mono_exception_walk_trace) (MonoException *ex, MonoInternalExceptionFrameWalk func, gpointer user_data);
        gboolean (*mono_install_handler_block_guard) (MonoThreadUnwindState *unwind_state);
+       gboolean (*mono_current_thread_has_handle_block_guard) (void);
 } MonoRuntimeExceptionHandlingCallbacks;
 
 /* used to free a dynamic method */
index 3f61428872f77097b9890ebc75896f26de35786d..0aede5fbb2092feb2ec7ad9c1b53ddfce4a1e903 100644 (file)
@@ -329,6 +329,7 @@ mono_runtime_class_init_full (MonoVTable *vtable, MonoError *error)
        MonoNativeThreadId tid;
        int do_initialization = 0;
        MonoDomain *last_domain = NULL;
+       MonoException * pending_tae = NULL;
 
        mono_error_init (error);
 
@@ -431,14 +432,22 @@ mono_runtime_class_init_full (MonoVTable *vtable, MonoError *error)
 
        if (do_initialization) {
                MonoException *exc = NULL;
+
+               mono_threads_begin_abort_protected_block ();
                mono_runtime_try_invoke (method, NULL, NULL, (MonoObject**) &exc, error);
-               if (exc != NULL && mono_error_ok (error)) {
-                       mono_error_set_exception_instance (error, exc);
-               }
+               mono_threads_end_abort_protected_block ();
+
+               //exception extracted, error will be set to the right value later
+               if (exc == NULL && !mono_error_ok (error))//invoking failed but exc was not set
+                       exc = mono_error_convert_to_exception (error);
+               else
+                       mono_error_cleanup (error);
+
+               mono_error_init (error);
 
                /* If the initialization failed, mark the class as unusable. */
                /* Avoid infinite loops */
-               if (!(mono_error_ok(error) ||
+               if (!(!exc ||
                          (klass->image == mono_defaults.corlib &&
                           !strcmp (klass->name_space, "System") &&
                           !strcmp (klass->name, "TypeInitializationException")))) {
@@ -451,15 +460,9 @@ mono_runtime_class_init_full (MonoVTable *vtable, MonoError *error)
 
                        MonoException *exc_to_throw = mono_get_exception_type_initialization_checked (full_name, exc, error);
                        g_free (full_name);
-                       return_val_if_nok (error, FALSE);
 
-                       mono_error_set_exception_instance (error, exc_to_throw);
+                       mono_error_assert_ok (error); //We can't recover from this, no way to fail a type we can't alloc a failure.
 
-                       MonoException *exc_to_store = mono_error_convert_to_exception (error);
-                       /* What we really want to do here is clone the error object and store one copy in the
-                        * domain's exception hash and use the other one to error out here. */
-                       mono_error_init (error);
-                       mono_error_set_exception_instance (error, exc_to_store);
                        /*
                         * Store the exception object so it could be thrown on subsequent
                         * accesses.
@@ -467,7 +470,7 @@ mono_runtime_class_init_full (MonoVTable *vtable, MonoError *error)
                        mono_domain_lock (domain);
                        if (!domain->type_init_exception_hash)
                                domain->type_init_exception_hash = mono_g_hash_table_new_type (mono_aligned_addr_hash, NULL, MONO_HASH_VALUE_GC, MONO_ROOT_SOURCE_DOMAIN, "type initialization exceptions table");
-                       mono_g_hash_table_insert (domain->type_init_exception_hash, klass, exc_to_store);
+                       mono_g_hash_table_insert (domain->type_init_exception_hash, klass, exc_to_throw);
                        mono_domain_unlock (domain);
                }
 
@@ -475,6 +478,11 @@ mono_runtime_class_init_full (MonoVTable *vtable, MonoError *error)
                        mono_domain_set (last_domain, TRUE);
                lock->done = TRUE;
                mono_type_init_unlock (lock);
+               if (exc && mono_object_class (exc) == mono_defaults.threadabortexception_class)
+                       pending_tae = exc;
+               //TAEs are blocked around .cctors, they must escape as soon as no cctor is left to run.
+               if (!pending_tae)
+                       pending_tae = mono_thread_try_resume_interruption ();
        } else {
                /* this just blocks until the initializing thread is done */
                mono_type_init_lock (lock);
@@ -495,7 +503,10 @@ mono_runtime_class_init_full (MonoVTable *vtable, MonoError *error)
                vtable->initialized = 1;
        mono_type_initialization_unlock ();
 
-       if (vtable->init_failed) {
+       //TAE wins over TIE
+       if (pending_tae)
+               mono_error_set_exception_instance (error, pending_tae);
+       else if (vtable->init_failed) {
                /* Either we were the initializing thread or we waited for the initialization */
                mono_error_set_exception_instance (error, get_type_init_exception_for_vtable (vtable));
                return FALSE;
@@ -4660,10 +4671,13 @@ mono_unhandled_exception (MonoObject *exc)
        if (!current_appdomain_delegate && !root_appdomain_delegate) {
                mono_print_unhandled_exception (exc);
        } else {
+               /* unhandled exception callbacks must not be aborted */
+               mono_threads_begin_abort_protected_block ();
                if (root_appdomain_delegate)
                        call_unhandled_exception_delegate (root_domain, root_appdomain_delegate, exc);
                if (current_appdomain_delegate)
                        call_unhandled_exception_delegate (current_domain, current_appdomain_delegate, exc);
+               mono_threads_end_abort_protected_block ();
        }
 
        /* set exitcode only if we will abort the process */
@@ -7533,14 +7547,16 @@ ves_icall_System_Runtime_Remoting_Messaging_AsyncResult_Invoke (MonoAsyncResult
 
                ac->msg->exc = NULL;
 
-               MonoError invoke_error;
-               res = mono_message_invoke (ares->async_delegate, ac->msg, &ac->msg->exc, &ac->out_args, &invoke_error);
+               res = mono_message_invoke (ares->async_delegate, ac->msg, &ac->msg->exc, &ac->out_args, &error);
+
+               /* The exit side of the invoke must not be aborted as it would leave the runtime in an undefined state */
+               mono_threads_begin_abort_protected_block ();
 
                if (!ac->msg->exc) {
-                       MonoException *ex = mono_error_convert_to_exception (&invoke_error);
+                       MonoException *ex = mono_error_convert_to_exception (&error);
                        ac->msg->exc = (MonoObject *)ex;
                } else {
-                       mono_error_cleanup (&invoke_error);
+                       mono_error_cleanup (&error);
                }
 
                MONO_OBJECT_SETREF (ac, res, res);
@@ -7554,11 +7570,14 @@ ves_icall_System_Runtime_Remoting_Messaging_AsyncResult_Invoke (MonoAsyncResult
                if (wait_event != NULL)
                        SetEvent (wait_event);
 
-               if (ac->cb_method) {
+               mono_error_init (&error); //the else branch would leave it in an undefined state
+               if (ac->cb_method)
                        mono_runtime_invoke_checked (ac->cb_method, ac->cb_target, (gpointer*) &ares, &error);
-                       if (mono_error_set_pending_exception (&error))
-                               return NULL;
-               }
+
+               mono_threads_end_abort_protected_block ();
+
+               if (mono_error_set_pending_exception (&error))
+                       return NULL;
        }
 
        return res;
index 132dcd84e61ba9d783cd0313982b09c47a3a0337..e27e89bb6f088f3df2316861be72abb028de114e 100644 (file)
@@ -258,4 +258,8 @@ mono_threads_attach_coop (MonoDomain *domain, gpointer *dummy);
 MONO_API void
 mono_threads_detach_coop (gpointer cookie, gpointer *dummy);
 
+void mono_threads_begin_abort_protected_block (void);
+void mono_threads_end_abort_protected_block (void);
+MonoException* mono_thread_try_resume_interruption (void);
+
 #endif /* _MONO_METADATA_THREADS_TYPES_H_ */
index 43dd5a430bcc4082614c473c1cbe4cb2fe25338e..e76f38a473fea6d941e6c05ed260bfd7f36f647e 100644 (file)
@@ -971,7 +971,7 @@ mono_thread_create_internal (MonoDomain *domain, gpointer func, gpointer arg, gb
        /* Check that the managed and unmanaged layout of MonoInternalThread matches */
 #ifndef MONO_CROSS_COMPILE
        if (mono_check_corlib_version () == NULL)
-               g_assert (((char*)&internal->unused2 - (char*)internal) == mono_defaults.internal_thread_class->fields [mono_defaults.internal_thread_class->field.count - 1].offset);
+               g_assert (((char*)&internal->abort_protected_block_count - (char*)internal) == mono_defaults.internal_thread_class->fields [mono_defaults.internal_thread_class->field.count - 1].offset);
 #endif
 
        return internal;
@@ -1085,7 +1085,7 @@ mono_thread_detach_internal (MonoInternalThread *thread)
        SET_CURRENT_OBJECT (NULL);
        mono_domain_unset ();
 
-       /* Don't need to CloseHandle this thread, even though we took a
+       /* Don't need to close the handle to this thread, even though we took a
         * reference in mono_thread_attach (), because the GC will do it
         * when the Thread object is finalised.
         */
@@ -1214,7 +1214,7 @@ ves_icall_System_Threading_InternalThread_Thread_free_internal (MonoInternalThre
         * when thread_cleanup () can be called after this.
         */
        if (thread)
-               CloseHandle (thread);
+               mono_threads_close_thread_handle (thread);
 
        if (this_obj->synch_cs) {
                MonoCoopMutex *synch_cs = this_obj->synch_cs;
@@ -2989,7 +2989,8 @@ struct wait_data
        guint32 num;
 };
 
-static void wait_for_tids (struct wait_data *wait, guint32 timeout)
+static void
+wait_for_tids (struct wait_data *wait, guint32 timeout)
 {
        guint32 i, ret;
        
@@ -3006,7 +3007,7 @@ static void wait_for_tids (struct wait_data *wait, guint32 timeout)
        }
        
        for(i=0; i<wait->num; i++)
-               CloseHandle (wait->handles[i]);
+               mono_threads_close_thread_handle (wait->handles [i]);
 
        if (ret == WAIT_TIMEOUT)
                return;
@@ -3069,7 +3070,7 @@ static void wait_for_tids_or_state_change (struct wait_data *wait, guint32 timeo
        }
        
        for(i=0; i<wait->num; i++)
-               CloseHandle (wait->handles[i]);
+               mono_threads_close_thread_handle (wait->handles [i]);
 
        if (ret == WAIT_TIMEOUT)
                return;
@@ -3389,7 +3390,7 @@ void mono_thread_suspend_all_other_threads (void)
                             || mono_gc_is_finalizer_internal_thread (thread)
                             || (thread->flags & MONO_THREAD_FLAG_DONT_MANAGE)
                        ) {
-                               //CloseHandle (wait->handles [i]);
+                               //mono_threads_close_thread_handle (wait->handles [i]);
                                wait->threads [i] = NULL; /* ignore this thread in next loop */
                                continue;
                        }
@@ -3400,7 +3401,7 @@ void mono_thread_suspend_all_other_threads (void)
                                (thread->state & ThreadState_StopRequested) != 0 ||
                                (thread->state & ThreadState_Stopped) != 0) {
                                UNLOCK_THREAD (thread);
-                               CloseHandle (wait->handles [i]);
+                               mono_threads_close_thread_handle (wait->handles [i]);
                                wait->threads [i] = NULL; /* ignore this thread in next loop */
                                continue;
                        }
@@ -4515,7 +4516,6 @@ mono_thread_request_interruption (gboolean running_managed)
                thread->state & ThreadState_Background)
                ExitThread (1);
 #endif
-       
        if (InterlockedCompareExchange (&thread->interruption_requested, 1, 0) == 1)
                return NULL;
        InterlockedIncrement (&thread_interruption_requested);
@@ -4794,6 +4794,13 @@ async_abort_critical (MonoThreadInfo *info, gpointer ud)
        if (mono_get_eh_callbacks ()->mono_install_handler_block_guard (mono_thread_info_get_suspend_state (info)))
                return MonoResumeThread;
 
+       /*
+       The target thread is running at least one protected block, which must not be interrupted, so we give up.
+       The protected block code will give them a chance when appropriate.
+       */
+       if (thread->abort_protected_block_count)
+               return MonoResumeThread;
+
        /*someone is already interrupting it*/
        if (InterlockedCompareExchange (&thread->interruption_requested, 1, 0) == 1)
                return MonoResumeThread;
@@ -4851,6 +4858,9 @@ self_abort_internal (MonoError *error)
        /* FIXME this is insanely broken, it doesn't cause interruption to happen synchronously
         * since passing FALSE to mono_thread_request_interruption makes sure it returns NULL */
 
+       /*
+       Self aborts ignore the protected block logic and raise the TAE regardless. This is verified by one of the tests in mono/tests/abort-cctor.cs.
+       */
        exc = mono_thread_request_interruption (TRUE);
        if (exc)
                mono_error_set_exception_instance (error, exc);
@@ -5175,3 +5185,36 @@ mono_threads_detach_coop (gpointer cookie, gpointer *dummy)
                }
        }
 }
+
+void
+mono_threads_begin_abort_protected_block (void)
+{
+       MonoInternalThread *thread;
+
+       thread = mono_thread_internal_current ();
+       ++thread->abort_protected_block_count;
+       mono_memory_barrier ();
+}
+
+void
+mono_threads_end_abort_protected_block (void)
+{
+       MonoInternalThread *thread;
+
+       thread = mono_thread_internal_current ();
+
+       mono_memory_barrier ();
+       --thread->abort_protected_block_count;
+}
+
+MonoException*
+mono_thread_try_resume_interruption (void)
+{
+       MonoInternalThread *thread;
+
+       thread = mono_thread_internal_current ();
+       if (thread->abort_protected_block_count || mono_get_eh_callbacks ()->mono_current_thread_has_handle_block_guard ())
+               return NULL;
+
+       return mono_thread_resume_interruption ();
+}
\ No newline at end of file
index 82059df3c3e79582f23118a4d31d22ce688687af..8d2f5bd17121d78ef98c4fba3c8e1f427d4b86da 100644 (file)
@@ -29,8 +29,40 @@ mono_main_with_options (int argc, char *argv [])
        return mono_main (argc, argv);
 }
 
-#define STREAM_INT(x) (*(uint32_t*)x)
-#define STREAM_LONG(x) (*(uint64_t*)x)
+#define STREAM_INT(x) GUINT32_TO_LE((*(uint32_t*)x))
+#define STREAM_LONG(x) GUINT64_TO_LE((*(uint64_t*)x))
+
+/**
+ * Loads a chunk of data from the file pointed to by the
+ * @fd starting at the file offset @offset for @size bytes
+ * and returns an allocated version of that string, or NULL
+ * on error.
+ */
+static char *
+load_from_region (int fd, uint64_t offset, uint64_t size)
+{
+       char *buffer;
+       off_t loc;
+       int status;
+       
+       do {
+               loc = lseek (fd, offset, SEEK_SET);
+       } while (loc == -1 && errno == EINTR);
+       if (loc == -1)
+               return NULL;
+       buffer = g_malloc (size + 1);
+       if (buffer == NULL)
+               return NULL;
+       buffer [size] = 0;
+       do {
+               status = read (fd, buffer, size);
+       } while (status == -1 && errno == EINTR);
+       if (status == -1){
+               g_free (buffer);
+               return NULL;
+       }
+       return buffer;
+}
 
 static gboolean
 probe_embedded (const char *program, int *ref_argc, char **ref_argv [])
@@ -104,15 +136,20 @@ probe_embedded (const char *program, int *ref_argc, char **ref_argv [])
                        char *config = kind + strlen ("config:");
                        char *aname = g_strdup (config);
                        aname [strlen(aname)-strlen(".config")] = 0;
-                       mono_register_config_for_assembly (aname, config);
+                       mono_register_config_for_assembly (aname, load_from_region (fd, offset, item_size));
                } else if (strncmp (kind, "systemconfig:", strlen ("systemconfig:")) == 0){
-                       mono_config_parse_memory (kind + strlen ("systemconfig:"));
+                       mono_config_parse_memory (load_from_region (fd, offset, item_size));
                } else if (strncmp (kind, "options:", strlen ("options:")) == 0){
-                       mono_parse_options_from (kind + strlen("options:"), ref_argc, ref_argv);
+                       mono_parse_options_from (load_from_region (fd, offset, item_size), ref_argc, ref_argv);
                } else if (strncmp (kind, "config_dir:", strlen ("config_dir:")) == 0){
-                       mono_set_dirs (getenv ("MONO_PATH"), kind + strlen ("config_dir:"));
+                       mono_set_dirs (getenv ("MONO_PATH"), load_from_region (fd, offset, item_size));
                } else if (strncmp (kind, "machineconfig:", strlen ("machineconfig:")) == 0) {
-                       mono_register_machine_config (kind + strlen ("machineconfig:"));
+                       mono_register_machine_config (load_from_region (fd, offset, item_size));
+               } else if (strncmp (kind, "env:", strlen ("env:")) == 0){
+                       char *data = load_from_region (fd, offset, item_size);
+                       uint8_t count = *data++;
+                       char *value = data + count + 1;
+                       g_setenv (data, value, FALSE);
                } else {
                        fprintf (stderr, "Unknown stream on embedded package: %s\n", kind);
                        exit (1);
index d16cee8cee3daa00962ced9e1ed97a12959fc445..2a437730a73b5d0e4057ec4f461a1bdefe98de4b 100644 (file)
@@ -94,6 +94,7 @@ static void restore_stack_protection (void);
 static void mono_walk_stack_full (MonoJitStackWalk func, MonoContext *start_ctx, MonoDomain *domain, MonoJitTlsData *jit_tls, MonoLMF *lmf, MonoUnwindOptions unwind_options, gpointer user_data);
 static void mono_raise_exception_with_ctx (MonoException *exc, MonoContext *ctx);
 static void mono_runtime_walk_stack_with_ctx (MonoJitStackWalk func, MonoContext *start_ctx, MonoUnwindOptions unwind_options, void *user_data);
+static gboolean mono_current_thread_has_handle_block_guard (void);
 
 void
 mono_exceptions_init (void)
@@ -136,6 +137,7 @@ mono_exceptions_init (void)
        cbs.mono_raise_exception_with_ctx = mono_raise_exception_with_ctx;
        cbs.mono_exception_walk_trace = mono_exception_walk_trace;
        cbs.mono_install_handler_block_guard = mono_install_handler_block_guard;
+       cbs.mono_current_thread_has_handle_block_guard = mono_current_thread_has_handle_block_guard;
        mono_install_eh_callbacks (&cbs);
 }
 
@@ -2676,6 +2678,13 @@ mono_install_handler_block_guard (MonoThreadUnwindState *ctx)
        return TRUE;
 }
 
+static gboolean
+mono_current_thread_has_handle_block_guard (void)
+{
+       MonoJitTlsData *jit_tls = (MonoJitTlsData *)mono_native_tls_get_value (mono_jit_tls_id);
+       return jit_tls && jit_tls->handler_block_return_address != NULL;
+}
+
 #else
 gboolean
 mono_install_handler_block_guard (MonoThreadUnwindState *ctx)
@@ -2683,6 +2692,12 @@ mono_install_handler_block_guard (MonoThreadUnwindState *ctx)
        return FALSE;
 }
 
+static gboolean
+mono_current_thread_has_handle_block_guard (void)
+{
+       return FALSE;
+}
+
 #endif
 
 void
index b4d9d0ad8666a1939bfd5a5ef04657a2832a2859..5a4c5206478a42be59a0fe0857158e03668252fb 100644 (file)
@@ -466,7 +466,8 @@ BASE_TEST_CS_SRC_UNIVERSAL=         \
        pinvoke_ppcf.cs \
        pinvoke_ppcd.cs \
        bug-29585.cs    \
-       priority.cs
+       priority.cs     \
+       abort-cctor.cs
 
 if INSTALL_MOBILE_STATIC
 BASE_TEST_CS_SRC= \
diff --git a/mono/tests/abort-cctor.cs b/mono/tests/abort-cctor.cs
new file mode 100644 (file)
index 0000000..34f8780
--- /dev/null
@@ -0,0 +1,332 @@
+using System;
+using System.Diagnostics;
+using System.Threading;
+using System.Runtime.CompilerServices;
+
+class Driver
+{
+       public static ManualResetEvent mre1 = new ManualResetEvent (false);
+       public static ManualResetEvent mre2 = new ManualResetEvent (false);
+
+       class StaticConstructor1
+       {
+               internal static bool gotToEnd, caughtException;
+               static StaticConstructor1 ()
+               {
+                       try {
+                               Console.WriteLine ("StaticConstructor1.StaticConstructor1 (1)");
+                               Driver.mre1.Set ();
+                               var sw = Stopwatch.StartNew ();
+                               Thread.Sleep (1000);
+                               sw.Stop ();
+                               typeof (string).GetMethods ();
+                               //XXX we assume that if we slept less than 900ms we got aborted
+                               if (sw.ElapsedMilliseconds < 900)
+                                       throw new Exception ("Bad abort broke our sleep");
+                               Console.WriteLine ("StaticConstructor1.StaticConstructor1 (2) waited {0}", sw.ElapsedMilliseconds);
+                               gotToEnd = true;
+                       } catch (Exception e) {
+                               caughtException = true;
+                               throw;
+                       }
+               }
+
+               public static void Init ()
+               {
+                       Console.WriteLine ("StaticConstructor1.Init");
+               }
+       }
+
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       static void IsStaticConstructor1Viable () {
+               new StaticConstructor1 ();
+               Console.WriteLine ("Did it get to the end? {0} Did it catch an exception {1}", StaticConstructor1.gotToEnd, StaticConstructor1.caughtException);
+               if (!StaticConstructor1.gotToEnd) /* the TAE must not land during a .cctor */
+                       Environment.Exit (1);
+               if (StaticConstructor1.caughtException)
+                       Environment.Exit (1);
+                       
+       }
+
+       static void Test1 ()
+       {
+               Console.WriteLine ("Test 1:");
+
+               Driver.mre1.Reset ();
+               Driver.mre2.Reset ();
+
+               Thread thread = new Thread (() => {
+                       try {
+                               StaticConstructor1.Init ();
+                       } catch (Exception e) {
+                               Console.WriteLine ("StaticConstructor1::init caught exception {0}", e);
+
+                               if (!(e is ThreadAbortException))
+                                       throw;
+                       }
+               });
+
+               thread.Start ();
+
+               Driver.mre1.WaitOne ();
+
+               // The ThreadAbortException should land while in
+               // the StaticConstructor1.cctor. The exception should
+               // be queued, and be rethrown when exiting the cctor.
+               thread.Abort ();
+
+               thread.Join ();
+
+               //is StaticConstructor1 viable?
+               try {
+                       IsStaticConstructor1Viable ();
+                       Console.WriteLine ("StaticConstructor1 is viable"); /* a TAE doesn't make a type unusable */
+               } catch (TypeInitializationException  e) {
+                       Console.WriteLine ("StaticConstructor1 not viable");
+                       Environment.Exit (1);
+               }
+       }
+
+       class StaticConstructor2Exception : Exception {}
+
+       class StaticConstructor2
+       {
+               static StaticConstructor2 ()
+               {
+                       Console.WriteLine ("StaticConstructor2.StaticConstructor2 (1)");
+                       Driver.mre1.Set ();
+                       throw new StaticConstructor2Exception ();
+                       /* Unreachable */
+                       Driver.mre2.Set ();
+                       Console.WriteLine ("StaticConstructor2.StaticConstructor2 (2)");
+               }
+
+               public static void Init ()
+               {
+                       Console.WriteLine ("StaticConstructor2.Init");
+               }
+       }
+
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       static void IsStaticConstructor2Viable () {
+               new StaticConstructor2 ();
+       }
+
+
+       static void Test2 ()
+       {
+               Console.WriteLine ("Test 2:");
+
+               Driver.mre1.Reset ();
+               Driver.mre2.Reset ();
+
+               Thread thread = new Thread (() => {
+                       try {
+                               StaticConstructor2.Init ();
+                       } catch (TypeInitializationException e) {
+                               Console.WriteLine (e);
+
+                               if (!(e.InnerException is StaticConstructor2Exception))
+                                       throw;
+                       }
+               });
+
+               thread.Start ();
+
+               Driver.mre1.WaitOne ();
+
+               // A InvalidOperationException should be thrown while in
+               // the StaticConstructor2.cctor. The exception should
+               // be wrapped in a TypeInitializationException.
+
+               if (Driver.mre2.WaitOne (500)) {
+                       /* We shouldn't reach Driver.mre.Set () in StaticConstructor2.cctor */
+                       Environment.Exit (1);
+               }
+
+               thread.Join ();
+
+               //is StaticConstructor2 viable?
+               try {
+                       IsStaticConstructor2Viable ();
+                       Console.WriteLine ("StaticConstructor2 is viable");
+                       /* A regular exception escaping the .cctor makes the type not usable */
+                       Environment.Exit (1);
+               } catch (TypeInitializationException e) {
+                       Console.WriteLine ("StaticConstructor2 not viable");
+               }
+
+       }
+
+       class StaticConstructor3
+       {
+               static StaticConstructor3 ()
+               {
+                       Console.WriteLine ("StaticConstructor3.StaticConstructor3 (1)");
+                       Driver.mre1.Set ();
+                       Thread.CurrentThread.Abort ();
+                       /* Unreachable */
+                       Driver.mre2.Set ();
+                       Console.WriteLine ("StaticConstructor3.StaticConstructor3 (2)");
+                       Environment.Exit (1);
+               }
+
+               public static void Init ()
+               {
+                       Console.WriteLine ("StaticConstructor3.Init");
+               }
+       }
+
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       static void IsStaticConstructor3Viable () {
+               new StaticConstructor3 ();
+       }
+
+       static void Test3 ()
+       {
+               Console.WriteLine ("Test 3:");
+
+               Driver.mre1.Reset ();
+               Driver.mre2.Reset ();
+
+               Thread thread = new Thread (() => {
+                       try {
+                               StaticConstructor3.Init ();
+                               Console.WriteLine ("cctor3 didn't throw?!?!");
+                               /* StaticConstructor3 self aborted */
+                               Environment.Exit (1);
+                       } catch (ThreadAbortException e) {
+                               Console.WriteLine ("TEST 3: aborted {0}", e);
+                       }
+               });
+
+               thread.Start ();
+
+               Driver.mre1.WaitOne ();
+
+               // A InvalidOperationException should be thrown while in
+               // the StaticConstructor2.cctor. The exception should
+               // be wrapped in a TypeInitializationException.
+
+               thread.Join ();
+
+               //is StaticConstructor2 viable?
+               try {
+                       IsStaticConstructor3Viable ();
+                       Console.WriteLine ("StaticConstructor3 is viable");
+                       /* A regular exception escaping the .cctor makes the type not usable */
+                       Environment.Exit (1);
+               } catch (TypeInitializationException e) {
+                       Console.WriteLine ("StaticConstructor3 not viable");
+               }
+       }
+
+
+
+
+
+       class StaticConstructor4
+       {
+               internal static bool gotToEnd, caughtException;
+
+               static StaticConstructor4 ()
+               {
+                       try {
+                               Console.WriteLine ("StaticConstructor4.StaticConstructor4 (1)");
+                               Driver.mre1.Set ();
+                               var sw = Stopwatch.StartNew ();
+                               Thread.Sleep (1000);
+                               sw.Stop ();
+                               typeof (string).GetMethods ();
+                               //XXX we assume that if we slept less than 900ms we got aborted
+                               if (sw.ElapsedMilliseconds < 900)
+                                       throw new Exception ("Bad abort broke our sleep");
+                               Console.WriteLine ("StaticConstructor4.StaticConstructor4 (2) waited {0}", sw.ElapsedMilliseconds);
+                               gotToEnd = true;
+                       } catch (Exception e) {
+                               caughtException = true;
+                               throw;
+                       }       
+               }
+
+               public static void Init ()
+               {
+                       Console.WriteLine ("StaticConstructor4.Init");
+               }
+       }
+
+       static bool got_to_the_end_of_the_finally = false;
+
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       static void IsStaticConstructor4Viable () {
+               new StaticConstructor4 ();
+               Console.WriteLine ("IsStaticConstructor4Viable: Did it get to the end? {0} Did it catch an exception {1} and end of the finally block {2}", StaticConstructor4.gotToEnd, StaticConstructor4.caughtException, got_to_the_end_of_the_finally);
+               if (!StaticConstructor4.gotToEnd) /* the TAE must not land during a .cctor */
+                       Environment.Exit (1);
+               if (StaticConstructor4.caughtException)
+                       Environment.Exit (1);
+       }
+
+       static void Test4 ()
+       {
+               Console.WriteLine ("Test 4:");
+
+               Driver.mre1.Reset ();
+               Driver.mre2.Reset ();
+
+               Thread thread = new Thread (() => {
+                       try {
+
+                               try {
+                               } finally {
+                                       StaticConstructor4.Init ();
+                                       Console.WriteLine ("Test 4: After the cctor");
+                                       got_to_the_end_of_the_finally = true;
+                               }
+                       } catch (Exception e) {
+                               Console.WriteLine ("StaticConstructor4::init caught exception {0}", e);
+                               if (!(e is ThreadAbortException))
+                                       throw;
+                               if (!got_to_the_end_of_the_finally)
+                                       throw new Exception ("Test 4: did not get to the end of the cctor");
+                       }
+               });
+
+               thread.Start ();
+
+               Driver.mre1.WaitOne ();
+
+               // The ThreadAbortException should land while in
+               // the StaticConstructor4.cctor. The exception should
+               // be queued, and be rethrown when exiting the cctor.
+               thread.Abort ();
+
+               thread.Join ();
+
+               if (!got_to_the_end_of_the_finally) { 
+                       Console.WriteLine ("Did not get to the end of test 4 cctor");
+                       Environment.Exit (1);
+               }
+
+               //is StaticConstructor4viable?
+               try {
+                       IsStaticConstructor4Viable ();
+                       Console.WriteLine ("StaticConstructor4 is viable"); /* a TAE doesn't make a type unusable */
+               } catch (TypeInitializationException  e) {
+                       Console.WriteLine ("StaticConstructor4 not viable");
+                       Environment.Exit (1);
+               }
+       }
+
+
+
+       public static int Main ()
+       {
+               Test1 ();
+               Test2 ();
+               Test3 ();
+               Test4 ();
+               Console.WriteLine ("done, all things good");
+               return 0;
+       }
+}
\ No newline at end of file
index c92f7bb316f814400e151a10f59a798ca5a74697..1a6b06fe0452fca72d97e2eb9bff4ffd3d22698d 100644 (file)
@@ -107,71 +107,18 @@ mono_threads_platform_register (MonoThreadInfo *info)
        info->handle = thread_handle_create ();
 }
 
-typedef struct {
-       void *(*start_routine)(void*);
-       void *arg;
-       int flags;
-       gint32 priority;
-       MonoCoopSem registered;
-       HANDLE handle;
-} StartInfo;
-
-static void*
-inner_start_thread (void *arg)
-{
-       StartInfo *start_info = (StartInfo *) arg;
-       void *t_arg = start_info->arg;
-       int res;
-       void *(*start_func)(void*) = start_info->start_routine;
-       guint32 flags = start_info->flags;
-       void *result;
-       MonoThreadInfo *info;
-
-       info = mono_thread_info_attach (&result);
-       info->runtime_thread = TRUE;
-
-       start_info->handle = info->handle;
-
-       mono_threads_platform_set_priority (info, start_info->priority);
-
-       if (flags & CREATE_SUSPENDED) {
-               info->create_suspended = TRUE;
-               mono_coop_sem_init (&info->create_suspended_sem, 0);
-       }
-
-       /* start_info is not valid after this */
-       mono_coop_sem_post (&(start_info->registered));
-       start_info = NULL;
-
-       if (flags & CREATE_SUSPENDED) {
-               res = mono_coop_sem_wait (&info->create_suspended_sem, MONO_SEM_FLAGS_NONE);
-               g_assert (res != -1);
-
-               mono_coop_sem_destroy (&info->create_suspended_sem);
-       }
-
-       /* Run the actual main function of the thread */
-       result = start_func (t_arg);
-
-       mono_threads_platform_exit (GPOINTER_TO_UINT (result));
-       g_assert_not_reached ();
-}
-
-HANDLE
-mono_threads_platform_create_thread (MonoThreadStart start_routine, gpointer arg, MonoThreadParm *tp, MonoNativeThreadId *out_tid)
+int
+mono_threads_platform_create_thread (MonoThreadStart thread_fn, gpointer thread_data, gsize stack_size, MonoNativeThreadId *out_tid)
 {
        pthread_attr_t attr;
-       int res;
        pthread_t thread;
-       StartInfo start_info;
-       guint32 stack_size;
-       int policy;
-       struct sched_param sp;
+       gint res;
 
        res = pthread_attr_init (&attr);
        g_assert (!res);
 
-       if (tp->stack_size == 0) {
+#ifdef HAVE_PTHREAD_ATTR_SETSTACKSIZE
+       if (stack_size == 0) {
 #if HAVE_VALGRIND_MEMCHECK_H
                if (RUNNING_ON_VALGRIND)
                        stack_size = 1 << 20;
@@ -180,52 +127,26 @@ mono_threads_platform_create_thread (MonoThreadStart start_routine, gpointer arg
 #else
                stack_size = (SIZEOF_VOID_P / 4) * 1024 * 1024;
 #endif
-       } else
-               stack_size = tp->stack_size;
+       }
 
 #ifdef PTHREAD_STACK_MIN
        if (stack_size < PTHREAD_STACK_MIN)
                stack_size = PTHREAD_STACK_MIN;
 #endif
 
-#ifdef HAVE_PTHREAD_ATTR_SETSTACKSIZE
        res = pthread_attr_setstacksize (&attr, stack_size);
        g_assert (!res);
-#endif
-
-       /*
-        * For policies that respect priorities set the prirority for the new thread
-        */ 
-       pthread_getschedparam(pthread_self(), &policy, &sp);
-       if ((policy == SCHED_FIFO) || (policy == SCHED_RR)) {
-               sp.sched_priority = win32_priority_to_posix_priority (tp->priority, policy);
-               res = pthread_attr_setschedparam (&attr, &sp);
-       }
-
-       memset (&start_info, 0, sizeof (StartInfo));
-       start_info.start_routine = (void *(*)(void *)) start_routine;
-       start_info.arg = arg;
-       start_info.flags = tp->creation_flags;
-       start_info.priority = tp->priority;
-       mono_coop_sem_init (&(start_info.registered), 0);
+#endif /* HAVE_PTHREAD_ATTR_SETSTACKSIZE */
 
        /* Actually start the thread */
-       res = mono_gc_pthread_create (&thread, &attr, inner_start_thread, &start_info);
-       if (res) {
-               mono_coop_sem_destroy (&(start_info.registered));
-               return NULL;
-       }
-
-       /* Wait until the thread register itself in various places */
-       res = mono_coop_sem_wait (&start_info.registered, MONO_SEM_FLAGS_NONE);
-       g_assert (res != -1);
-
-       mono_coop_sem_destroy (&(start_info.registered));
+       res = mono_gc_pthread_create (&thread, &attr, (gpointer (*)(gpointer)) thread_fn, thread_data);
+       if (res)
+               return -1;
 
        if (out_tid)
                *out_tid = thread;
 
-       return start_info.handle;
+       return 0;
 }
 
 gboolean
@@ -274,6 +195,12 @@ mono_threads_platform_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
        return handle;
 }
 
+void
+mono_threads_platform_close_thread_handle (HANDLE handle)
+{
+       mono_w32handle_unref (handle);
+}
+
 int
 mono_threads_pthread_kill (MonoThreadInfo *info, int signum)
 {
index d2b3b5e2ea357281d6e7b9ce9e47c6edf496e1d1..bb8d300a58b59306e44268214dd1ed46a932a1ef 100644 (file)
@@ -148,84 +148,24 @@ mono_threads_platform_register (MonoThreadInfo *info)
        info->handle = thread_handle;
 }
 
-typedef struct {
-       LPTHREAD_START_ROUTINE start_routine;
-       void *arg;
-       gint32 priority;
-       MonoCoopSem registered;
-       gboolean suspend;
-       HANDLE handle;
-} ThreadStartInfo;
-
-static DWORD WINAPI
-inner_start_thread (LPVOID arg)
+int
+mono_threads_platform_create_thread (MonoThreadStart thread_fn, gpointer thread_data, gsize stack_size, MonoNativeThreadId *out_tid)
 {
-       ThreadStartInfo *start_info = arg;
-       void *t_arg = start_info->arg;
-       LPTHREAD_START_ROUTINE start_func = start_info->start_routine;
-       DWORD result;
-       gboolean suspend = start_info->suspend;
-       MonoThreadInfo *info;
-       int res;
-
-       info = mono_thread_info_attach (&result);
-       info->runtime_thread = TRUE;
-
-       start_info->handle = info->handle;
-
-       mono_threads_platform_set_priority(info, start_info->priority);
-
-       if (suspend) {
-               info->create_suspended = TRUE;
-               mono_coop_sem_init (&info->create_suspended_sem, 0);
-       }
-
-       mono_coop_sem_post (&(start_info->registered));
-
-       if (suspend) {
-               res = mono_coop_sem_wait (&info->create_suspended_sem, MONO_SEM_FLAGS_NONE);
-               g_assert (res != -1);
-
-               mono_coop_sem_destroy (&info->create_suspended_sem);
-       }
-
-       result = start_func (t_arg);
+       HANDLE result;
+       DWORD thread_id;
 
-       mono_thread_info_detach ();
+       result = CreateThread (NULL, stack_size, (LPTHREAD_START_ROUTINE) thread_fn, thread_data, 0, &thread_id);
+       if (!result)
+               return -1;
 
-       return result;
-}
+       /* A new handle is open when attaching
+        * the thread, so we don't need this one */
+       CloseHandle (result);
 
-HANDLE
-mono_threads_platform_create_thread (MonoThreadStart start_routine, gpointer arg, MonoThreadParm *tp, MonoNativeThreadId *out_tid)
-{
-       ThreadStartInfo start_info;
-       HANDLE result;
-       DWORD thread_id;
-       guint32 creation_flags = tp->creation_flags;
-       int res;
-
-       memset (&start_info, 0, sizeof (start_info));
-       mono_coop_sem_init (&(start_info.registered), 0);
-       start_info.arg = arg;
-       start_info.start_routine = start_routine;
-       start_info.suspend = creation_flags & CREATE_SUSPENDED;
-       start_info.priority = tp->priority;
-       creation_flags &= ~CREATE_SUSPENDED;
-
-       result = CreateThread (NULL, tp->stack_size, inner_start_thread, &start_info, creation_flags, &thread_id);
-       if (result) {
-               res = mono_coop_sem_wait (&(start_info.registered), MONO_SEM_FLAGS_NONE);
-               g_assert (res != -1);
-
-               /* A new handle has been opened when attaching
-                * the thread, so we don't need this one */
-               CloseHandle (result);
-       }
        if (out_tid)
                *out_tid = thread_id;
-       mono_coop_sem_destroy (&(start_info.registered));
-       return start_info.handle;
+
+       return 0;
 }
 
 
@@ -319,6 +259,12 @@ mono_threads_platform_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
        return OpenThread (THREAD_ALL_ACCESS, TRUE, tid);
 }
 
+void
+mono_threads_platform_close_thread_handle (HANDLE handle)
+{
+       CloseHandle (handle);
+}
+
 #if defined(_MSC_VER)
 const DWORD MS_VC_EXCEPTION=0x406D1388;
 #pragma pack(push,8)
index fe503bf9f1bfb2d8eb956b089a39c41833edde50..0611e739d3772bbedc9b1e606c2bac84585a696c 100644 (file)
@@ -1138,6 +1138,75 @@ mono_thread_info_is_async_context (void)
                return FALSE;
 }
 
+typedef struct {
+       gint32 ref;
+       MonoThreadStart start_routine;
+       gpointer start_routine_arg;
+       gboolean create_suspended;
+       gint32 priority;
+       MonoCoopSem registered;
+       MonoThreadInfo *info;
+} CreateThreadData;
+
+static gsize WINAPI
+inner_start_thread (gpointer data)
+{
+       CreateThreadData *thread_data;
+       MonoThreadInfo *info;
+       MonoThreadStart start_routine;
+       gpointer start_routine_arg;
+       guint32 start_routine_res;
+       gboolean create_suspended;
+       gint32 priority;
+       gsize dummy;
+       gint res;
+
+       thread_data = (CreateThreadData*) data;
+       g_assert (thread_data);
+
+       start_routine = thread_data->start_routine;
+       start_routine_arg = thread_data->start_routine_arg;
+
+       create_suspended = thread_data->create_suspended;
+       priority = thread_data->priority;
+
+       info = mono_thread_info_attach (&dummy);
+       info->runtime_thread = TRUE;
+
+       mono_threads_platform_set_priority (info, priority);
+
+       if (create_suspended) {
+               info->create_suspended = TRUE;
+               mono_coop_sem_init (&info->create_suspended_sem, 0);
+       }
+
+       thread_data->info = info;
+
+       mono_coop_sem_post (&thread_data->registered);
+
+       if (InterlockedDecrement (&thread_data->ref) == 0) {
+               mono_coop_sem_destroy (&thread_data->registered);
+               g_free (thread_data);
+       }
+
+       /* thread_data is not valid anymore */
+       thread_data = NULL;
+
+       if (create_suspended) {
+               res = mono_coop_sem_wait (&info->create_suspended_sem, MONO_SEM_FLAGS_NONE);
+               g_assert (res == 0);
+
+               mono_coop_sem_destroy (&info->create_suspended_sem);
+       }
+
+       /* Run the actual main function of the thread */
+       start_routine_res = start_routine (start_routine_arg);
+
+       mono_threads_platform_exit (start_routine_res);
+
+       g_assert_not_reached ();
+}
+
 /*
  * mono_threads_create_thread:
  *
@@ -1147,7 +1216,43 @@ mono_thread_info_is_async_context (void)
 HANDLE
 mono_threads_create_thread (MonoThreadStart start, gpointer arg, MonoThreadParm *tp, MonoNativeThreadId *out_tid)
 {
-       return mono_threads_platform_create_thread (start, arg, tp, out_tid);
+       CreateThreadData *thread_data;
+       MonoThreadInfo *info;
+       gint res;
+       gpointer ret;
+
+       thread_data = g_new0 (CreateThreadData, 1);
+       thread_data->ref = 2;
+       thread_data->start_routine = start;
+       thread_data->start_routine_arg = arg;
+       thread_data->create_suspended = tp->creation_flags & CREATE_SUSPENDED;
+       thread_data->priority = tp->priority;
+       mono_coop_sem_init (&thread_data->registered, 0);
+
+       res = mono_threads_platform_create_thread (inner_start_thread, (gpointer) thread_data, tp->stack_size, out_tid);
+       if (res != 0) {
+               /* ref is not going to be decremented in inner_start_thread */
+               InterlockedDecrement (&thread_data->ref);
+               ret = NULL;
+               goto done;
+       }
+
+       res = mono_coop_sem_wait (&thread_data->registered, MONO_SEM_FLAGS_NONE);
+       g_assert (res == 0);
+
+       info = thread_data->info;
+       g_assert (info);
+
+       ret = info->handle;
+       g_assert (ret);
+
+done:
+       if (InterlockedDecrement (&thread_data->ref) == 0) {
+               mono_coop_sem_destroy (&thread_data->registered);
+               g_free (thread_data);
+       }
+
+       return ret;
 }
 
 /*
@@ -1364,6 +1469,12 @@ mono_threads_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
        return mono_threads_platform_open_thread_handle (handle, tid);
 }
 
+void
+mono_threads_close_thread_handle (HANDLE handle)
+{
+       return mono_threads_platform_close_thread_handle (handle);
+}
+
 #define INTERRUPT_STATE ((MonoThreadInfoInterruptToken*) (size_t) -1)
 
 struct _MonoThreadInfoInterruptToken {
index a59430e855cf2b374f77fd96db6022890b693862..93e4aa4ce0fc1108402909aafa22af59b37b2782 100644 (file)
@@ -473,6 +473,9 @@ mono_threads_get_max_stack_size (void);
 HANDLE
 mono_threads_open_thread_handle (HANDLE handle, MonoNativeThreadId tid);
 
+void
+mono_threads_close_thread_handle (HANDLE handle);
+
 MONO_API void
 mono_threads_attach_tools_thread (void);
 
@@ -535,11 +538,12 @@ gboolean mono_threads_suspend_needs_abort_syscall (void);
 
 void mono_threads_platform_register (THREAD_INFO_TYPE *info);
 void mono_threads_platform_unregister (THREAD_INFO_TYPE *info);
-HANDLE mono_threads_platform_create_thread (MonoThreadStart start, gpointer arg, MonoThreadParm *, MonoNativeThreadId *out_tid);
+int mono_threads_platform_create_thread (MonoThreadStart thread_fn, gpointer thread_data, gsize stack_size, MonoNativeThreadId *out_tid);
 void mono_threads_platform_get_stack_bounds (guint8 **staddr, size_t *stsize);
 gboolean mono_threads_platform_yield (void);
 void mono_threads_platform_exit (int exit_code);
 HANDLE mono_threads_platform_open_thread_handle (HANDLE handle, MonoNativeThreadId tid);
+void mono_threads_platform_close_thread_handle (HANDLE handle);
 void mono_threads_platform_set_exited (THREAD_INFO_TYPE *info);
 void mono_threads_platform_describe (THREAD_INFO_TYPE *info, GString *text);
 void mono_threads_platform_own_mutex (THREAD_INFO_TYPE *info, gpointer mutex_handle);