al.1 \
cert2spc.1 \
certmgr.1 \
+ cert-sync.1 \
chktrust.1 \
cilc.1 \
crlupdate.1 \
--- /dev/null
+.\"
+.\" 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)
$ 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
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.
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
// 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
#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
#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
#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
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;
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;
}
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 {
[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;
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);
}
}
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);
}
}
[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;
}
}
{
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
+ }
}
}
// 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
+ }
}
}
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);
}
}
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,
),
binder.ReturnType
);
+#endif
resultMO = new DynamicMetaObject(checkedConvert, resultMO.Restrictions);
}
--- /dev/null
+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
</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">
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",
""
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;
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)
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;
" --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" +
* same field there.
*/
gsize start_notify_refcount;
- gpointer unused2;
+ gsize abort_protected_block_count;
};
struct _MonoThread {
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 */
MonoNativeThreadId tid;
int do_initialization = 0;
MonoDomain *last_domain = NULL;
+ MonoException * pending_tae = NULL;
mono_error_init (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")))) {
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.
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);
}
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);
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;
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 */
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);
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;
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_ */
/* 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;
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.
*/
* 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;
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;
}
for(i=0; i<wait->num; i++)
- CloseHandle (wait->handles[i]);
+ mono_threads_close_thread_handle (wait->handles [i]);
if (ret == WAIT_TIMEOUT)
return;
}
for(i=0; i<wait->num; i++)
- CloseHandle (wait->handles[i]);
+ mono_threads_close_thread_handle (wait->handles [i]);
if (ret == WAIT_TIMEOUT)
return;
|| 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;
}
(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;
}
thread->state & ThreadState_Background)
ExitThread (1);
#endif
-
if (InterlockedCompareExchange (&thread->interruption_requested, 1, 0) == 1)
return NULL;
InterlockedIncrement (&thread_interruption_requested);
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;
/* 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);
}
}
}
+
+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
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 [])
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);
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)
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);
}
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)
return FALSE;
}
+static gboolean
+mono_current_thread_has_handle_block_guard (void)
+{
+ return FALSE;
+}
+
#endif
void
pinvoke_ppcf.cs \
pinvoke_ppcd.cs \
bug-29585.cs \
- priority.cs
+ priority.cs \
+ abort-cctor.cs
if INSTALL_MOBILE_STATIC
BASE_TEST_CS_SRC= \
--- /dev/null
+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
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;
#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
return handle;
}
+void
+mono_threads_platform_close_thread_handle (HANDLE handle)
+{
+ mono_w32handle_unref (handle);
+}
+
int
mono_threads_pthread_kill (MonoThreadInfo *info, int signum)
{
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;
}
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)
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:
*
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;
}
/*
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 {
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);
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);