Run all managed code in a subthread. Re-enable GC threaded finalisation.
authorDick Porter <dick@acm.org>
Thu, 23 Jan 2003 17:46:26 +0000 (17:46 -0000)
committerDick Porter <dick@acm.org>
Thu, 23 Jan 2003 17:46:26 +0000 (17:46 -0000)
Fixes bugs 34263 and 31333.

(All tests still behave the same way - 4 fails)

2003-01-23  Dick Porter  <dick@ximian.com>

* threads.c (start_wrapper): Create a Thread object if needed, so
the Main() thread can do the class initialisation in a subthread
that has been set up to allow managed code execution.

Pass the thread ID instead of the MonoThread pointer to the thread
start and attach callbacks.  This change is required, because the
jit thread start callback must be called _before_ the Thread
object can be created.

(mono_thread_init): Removed much object creation code that is no
longer needed.  No managed code is called from here now.

* object.c (mono_runtime_exec_managed_code): Create a subthread
for Main, and call back to the runtime to use it.
Set the exit code when Main exits.

* gc.c: Make sure domain finalisation happens in a subthread.
Re-enable threaded GC, fixing bug 31333 (again).

* environment.c: System.Environment internall calls (so far just
ExitCode is here, the others are still in icall.c)

* appdomain.c (mono_runtime_cleanup): All threads running managed
code should have finished before mono_runtime_cleanup() is
reached, so no need to clean up threads.

2003-01-23  Dick Porter  <dick@ximian.com>

* mono.c: Use mono_runtime_exec_managed_code() to run all managed
code in a subthread.

* jit.c: Changed thread start and attach callbacks to pass the
thread ID, not the MonoThread pointer.  Arrange that managed code
execution will fail an assertion in the main thread, just to be
sure.

2003-01-23  Dick Porter  <dick@ximian.com>

* interp.c: Use mono_runtime_exec_managed_code() to run all
managed code in a subthread.

svn path=/trunk/mono/; revision=10838

19 files changed:
mono/interpreter/ChangeLog
mono/interpreter/interp.c
mono/jit/ChangeLog
mono/jit/jit.c
mono/jit/mono.c
mono/metadata/ChangeLog
mono/metadata/Makefile.am
mono/metadata/appdomain.c
mono/metadata/appdomain.h
mono/metadata/environment.c [new file with mode: 0644]
mono/metadata/environment.h [new file with mode: 0644]
mono/metadata/gc.c
mono/metadata/icall.c
mono/metadata/object.c
mono/metadata/object.h
mono/metadata/threadpool.c
mono/metadata/threads.c
mono/metadata/threads.h
samples/embed/teste.c

index a1f6a0825593a6b99448cf8671142127d86d5033..a981e2aa3bcd337c2d229bb67d1f92b490e269bd 100644 (file)
@@ -1,3 +1,8 @@
+2003-01-23  Dick Porter  <dick@ximian.com>
+
+       * interp.c: Use mono_runtime_exec_managed_code() to run all
+       managed code in a subthread.
+
 2002-11-15  Dick Porter  <dick@ximian.com>
 
        * interp.c: mono_runtime_init() now has an extra parameter for
index 9dff81d86c67948761140eafc7a3744d9b2ed57d..159aee3cdb48de6f39fc6bb8bcea10767989fb4d 100644 (file)
@@ -54,6 +54,7 @@
 #include <mono/metadata/socket-io.h>
 #include <mono/metadata/mono-config.h>
 #include <mono/metadata/marshal.h>
+#include <mono/metadata/environment.h>
 #include <mono/os/util.h>
 
 /*#include <mono/cli/types.h>*/
@@ -4414,14 +4415,53 @@ ves_icall_get_trace (MonoException *exc, gint32 skip, MonoBoolean need_file_info
        return NULL;
 }
 
+typedef struct
+{
+       MonoDomain *domain;
+       char *file;
+       int argc;
+       char **argv;
+} MainThreadArgs;
+
+static void main_thread_handler (gpointer user_data)
+{
+       MainThreadArgs *main_args=(MainThreadArgs *)user_data;
+       MonoAssembly *assembly;
+       char *error;
+
+       assembly = mono_domain_assembly_open (main_args->domain,
+                                             main_args->file);
+
+       if (!assembly){
+               fprintf (stderr, "Can not open image %s\n", main_args->file);
+               exit (1);
+       }
+
+
+#ifdef RUN_TEST
+       test_load_class (assembly->image);
+#else
+       error = mono_verify_corlib ();
+       if (error) {
+               fprintf (stderr, "Corlib not in sync with this runtime: %s\n", error);
+               exit (1);
+       }
+       segv_exception = mono_get_exception_null_reference ();
+       segv_exception->message = mono_string_new (main_args->domain, "Segmentation fault");
+       signal (SIGSEGV, segv_handler);
+
+       ves_exec (main_args->domain, assembly, main_args->argc, main_args->argv);
+#endif
+}
+
 int 
 main (int argc, char *argv [])
 {
        MonoDomain *domain;
-       MonoAssembly *assembly;
        int retval = 0, i, ocount = 0;
-       char *file, *error, *config_file = NULL;
-
+       char *file, *config_file = NULL;
+       MainThreadArgs main_args;
+       
        if (argc < 2)
                usage ();
 
@@ -4489,33 +4529,22 @@ main (int argc, char *argv [])
        domain = mono_init (file);
        mono_runtime_init (domain, NULL, NULL);
 
-       assembly = mono_domain_assembly_open (domain, file);
-
-       if (!assembly){
-               fprintf (stderr, "Can not open image %s\n", file);
-               exit (1);
-       }
-
-
-#ifdef RUN_TEST
-       test_load_class (assembly->image);
-#else
-       error = mono_verify_corlib ();
-       if (error) {
-               fprintf (stderr, "Corlib not in sync with this runtime: %s\n", error);
-               exit (1);
-       }
-       segv_exception = mono_get_exception_null_reference ();
-       segv_exception->message = mono_string_new (domain, "Segmentation fault");
-       signal (SIGSEGV, segv_handler);
-
-       retval = ves_exec (domain, assembly, argc - i, argv + i);
-#endif
+       main_args.domain=domain;
+       main_args.file=file;
+       main_args.argc=argc-i;
+       main_args.argv=argv+i;
+       
+       mono_runtime_exec_managed_code (domain, main_thread_handler,
+                                       &main_args);
+       
        mono_profiler_shutdown ();
        
        mono_runtime_cleanup (domain);
        mono_domain_unload (domain, TRUE);
 
+       /* Get the return value from System.Environment.ExitCode */
+       retval=mono_environment_exitcode_get ();
+
 #if DEBUG_INTERP
        if (ocount) {
                fprintf (stderr, "opcode count: %ld\n", opcode_count);
index 5540ce8c928a25f864b272a58885d196eb0cd47a..2206ba51fdb8afc6b3c56fe1481ed8d35310e6f2 100644 (file)
@@ -1,3 +1,13 @@
+2003-01-23  Dick Porter  <dick@ximian.com>
+
+       * mono.c: Use mono_runtime_exec_managed_code() to run all managed
+       code in a subthread.
+
+       * jit.c: Changed thread start and attach callbacks to pass the
+       thread ID, not the MonoThread pointer.  Arrange that managed code
+       execution will fail an assertion in the main thread, just to be
+       sure.
+
 2003-01-22  Martin Baulig  <martin@ximian.com>
 
        * debug.c: Reverted Gonzalo's last change since it is wrong.
index 116865e57c98e5265304069d13295ba8b7244cdf..1886ed4d5283e0fd83b45f708019b6b0adb33cd6 100644 (file)
@@ -4086,7 +4086,7 @@ mono_thread_abort (MonoObject *obj)
 }
 
 static void
-mono_thread_start_cb (MonoThread *thread, gpointer stack_start, gpointer func)
+mono_thread_start_cb (guint32 tid, gpointer stack_start, gpointer func)
 {
        MonoJitTlsData *jit_tls;
        MonoLMF *lmf;
@@ -4103,7 +4103,7 @@ mono_thread_start_cb (MonoThread *thread, gpointer stack_start, gpointer func)
 
        jit_tls->lmf = lmf;
 
-       mono_debugger_event (MONO_DEBUGGER_EVENT_THREAD_CREATED, thread, func);
+       mono_debugger_event (MONO_DEBUGGER_EVENT_THREAD_CREATED, (gpointer)tid, func);
 }
 
 void (*mono_thread_attach_aborted_cb ) (MonoObject *obj) = NULL;
@@ -4118,7 +4118,7 @@ mono_thread_abort_dummy (MonoObject *obj)
 }
 
 static void
-mono_thread_attach_cb (MonoThread *thread, gpointer stack_start)
+mono_thread_attach_cb (guint32 tid, gpointer stack_start)
 {
        MonoJitTlsData *jit_tls;
        MonoLMF *lmf;
@@ -4212,7 +4212,13 @@ mono_jit_init (const char *file) {
        InitializeCriticalSection (metadata_section);
 
        mono_jit_tls_id = TlsAlloc ();
-       mono_thread_start_cb (NULL, (gpointer)-1, NULL);
+
+       /* Don't set up the main thread for managed code execution -
+        * this will give a handy assertion fail in
+        * mono_get_lmf_addr() if any buggy runtime code tries to run
+        * managed code in this thread.
+        */
+       /* mono_thread_start_cb (GetCurrentThreadId (), (gpointer)-1, NULL); */
 
        mono_install_compile_method (mono_jit_compile_method);
        mono_install_trampoline (arch_create_jit_trampoline);
@@ -4231,12 +4237,6 @@ mono_jit_init (const char *file) {
 void
 mono_jit_cleanup (MonoDomain *domain)
 {
-
-       /* 
-        * mono_runtime_cleanup() needs to be called early since
-        * it needs the execution engine still fully working (it will
-        * wait for other threads to finish).
-        */
        mono_runtime_cleanup (domain);
 
        mono_domain_finalize (domain);
index 5a77c7920c1828b4191b087583787984902e144c..44c06374aa8ca9702d307fd0adf4ac022cba2ed3 100644 (file)
@@ -16,6 +16,7 @@
 #include "mono/metadata/threadpool.h"
 #include "mono/metadata/mono-config.h"
 #include <mono/metadata/profiler-private.h>
+#include <mono/metadata/environment.h>
 #include <mono/os/util.h>
 #include <locale.h>
 
@@ -185,11 +186,73 @@ usage (char *name)
        exit (1);
 }
 
+typedef struct 
+{
+       MonoDomain *domain;
+       char *file;
+       gboolean testjit;
+       char *debug_args;
+       char *compile_class;
+       int compile_times;
+       GList *precompile_classes;
+       int verbose;
+       int break_on_main;
+       int argc;
+       char **argv;
+} MainThreadArgs;
+
+static void main_thread_handler (gpointer user_data)
+{
+       MainThreadArgs *main_args=(MainThreadArgs *)user_data;
+       MonoAssembly *assembly;
+       MonoDebugHandle *debug = NULL;
+       
+       assembly = mono_domain_assembly_open (main_args->domain,
+                                             main_args->file);
+       if (!assembly){
+               fprintf (stderr, "Can not open image %s\n", main_args->file);
+               exit (1);
+       }
+
+       if (mono_debug_format != MONO_DEBUG_FORMAT_NONE) {
+               gchar **args;
+
+               args = g_strsplit (main_args->debug_args ?
+                                  main_args->debug_args : "", ",", -1);
+               debug = mono_debug_open (assembly, mono_debug_format, (const char **) args);
+               g_strfreev (args);
+       }
+
+       if (main_args->testjit) {
+               mono_jit_compile_image (assembly->image, TRUE);
+       } else if (main_args->compile_class) {
+               mono_jit_compile_class (assembly, main_args->compile_class, main_args->compile_times, TRUE);
+       } else {
+               GList *tmp;
+
+               for (tmp = main_args->precompile_classes; tmp; tmp = tmp->next)
+                       mono_jit_compile_class (assembly, tmp->data, 1,
+                                               main_args->verbose);
+
+               if (main_args->break_on_main) {
+                       MonoImage *image = assembly->image;
+                       MonoMethodDesc *desc;
+                       MonoMethod *method;
+
+                       method = mono_get_method (image, mono_image_get_entry_point (image), NULL);
+                       desc = mono_method_desc_from_method (method);
+                       mono_insert_breakpoint_full (desc, FALSE);
+               }
+
+               mono_jit_exec (main_args->domain, assembly, main_args->argc,
+                              main_args->argv);
+       }
+}
+
 int 
 main (int argc, char *argv [])
 {
        MonoDomain *domain;
-       MonoAssembly *assembly;
        int retval = 0, i;
        int compile_times = 1000;
        char *compile_class = NULL;
@@ -199,8 +262,7 @@ main (int argc, char *argv [])
        int verbose = FALSE;
        GList *precompile_classes = NULL;
        int break_on_main = FALSE;
-       MonoDebugHandle *debug = NULL;
-
+       MainThreadArgs main_args;
        
        setlocale(LC_ALL, "");
        g_log_set_always_fatal (G_LOG_LEVEL_ERROR);
@@ -309,46 +371,27 @@ main (int argc, char *argv [])
                fprintf (stderr, "Corlib not in sync with this runtime: %s\n", error);
                exit (1);
        }
-
-       assembly = mono_domain_assembly_open (domain, file);
-       if (!assembly){
-               fprintf (stderr, "Can not open image %s\n", file);
-               exit (1);
-       }
-
-       if (mono_debug_format != MONO_DEBUG_FORMAT_NONE) {
-               gchar **args;
-
-               args = g_strsplit (debug_args ? debug_args : "", ",", -1);
-               debug = mono_debug_open (assembly, mono_debug_format, (const char **) args);
-               g_strfreev (args);
-       }
-
-       if (testjit) {
-               mono_jit_compile_image (assembly->image, TRUE);
-       } else if (compile_class) {
-               mono_jit_compile_class (assembly, compile_class, compile_times, TRUE);
-       } else {
-               GList *tmp;
-
-               for (tmp = precompile_classes; tmp; tmp = tmp->next)
-                       mono_jit_compile_class (assembly, tmp->data, 1, verbose);
-
-               if (break_on_main) {
-                       MonoImage *image = assembly->image;
-                       MonoMethodDesc *desc;
-                       MonoMethod *method;
-
-                       method = mono_get_method (image, mono_image_get_entry_point (image), NULL);
-                       desc = mono_method_desc_from_method (method);
-                       mono_insert_breakpoint_full (desc, FALSE);
-               }
-
-               retval = mono_jit_exec (domain, assembly, argc - i, argv + i);
-       }
+       
+       main_args.domain=domain;
+       main_args.file=file;
+       main_args.testjit=testjit;
+       main_args.debug_args=debug_args;
+       main_args.compile_class=compile_class;
+       main_args.compile_times=compile_times;
+       main_args.precompile_classes=precompile_classes;
+       main_args.verbose=verbose;
+       main_args.break_on_main=break_on_main;
+       main_args.argc=argc-i;
+       main_args.argv=argv+i;
+       
+       mono_runtime_exec_managed_code (domain, main_thread_handler,
+                                       &main_args);
 
        mono_profiler_shutdown ();
        mono_jit_cleanup (domain);
 
+       /* Look up return value from System.Environment.ExitCode */
+       retval=mono_environment_exitcode_get ();
+       
        return retval;
 }
index 0f2e85b9110d7a1b2c1b45e4c29467ed5054ccab..82733ece844d596cfa4122fad27e0b4340f2f920 100644 (file)
@@ -1,3 +1,31 @@
+2003-01-23  Dick Porter  <dick@ximian.com>
+
+       * threads.c (start_wrapper): Create a Thread object if needed, so
+       the Main() thread can do the class initialisation in a subthread
+       that has been set up to allow managed code execution.
+
+       Pass the thread ID instead of the MonoThread pointer to the thread
+       start and attach callbacks.  This change is required, because the
+       jit thread start callback must be called _before_ the Thread
+       object can be created.
+       
+       (mono_thread_init): Removed much object creation code that is no
+       longer needed.  No managed code is called from here now.
+
+       * object.c (mono_runtime_exec_managed_code): Create a subthread
+       for Main, and call back to the runtime to use it.
+       Set the exit code when Main exits.
+
+       * gc.c: Make sure domain finalisation happens in a subthread.
+       Re-enable threaded GC, fixing bug 31333 (again).
+
+       * environment.c: System.Environment internall calls (so far just
+       ExitCode is here, the others are still in icall.c)
+
+       * appdomain.c (mono_runtime_cleanup): All threads running managed
+       code should have finished before mono_runtime_cleanup() is
+       reached, so no need to clean up threads.
+
 2003-01-22  Martin Baulig  <martin@ximian.com>
 
        * appdomain.h (MonoThreadStartCB): Added `MonoThread *thread' and
index 90f3298e07b23936a27f2494643b1abebc71d327..fe5a4f52bc1a52930c2ddefc41cce205683c0af8 100644 (file)
@@ -48,7 +48,9 @@ libmonoruntime_la_SOURCES = \
        sysmath.h       \
        sysmath.c       \
        process.c       \
-       process.h
+       process.h       \
+       environment.c   \
+       environment.h
 
 libmetadata_la_SOURCES = \
        assembly.c      \
@@ -75,7 +77,8 @@ libmonoruntimeinclude_HEADERS = \
        debug-mono-symfile.h    \
        threadpool.h    \
        threads-types.h \
-       threads.h
+       threads.h       \
+       environment.h
 
 libmetadatainclude_HEADERS = \
        assembly.h      \
index 68038da50eb94ee2693a8b43df0c6ff744405e60..55986e0d76896bcda8299d1e39cff26df6089919 100644 (file)
@@ -72,7 +72,7 @@ mono_runtime_init (MonoDomain *domain, MonoThreadStartCB start_cb,
        g_assert (mono_delegate_semaphore != INVALID_HANDLE_VALUE);
        InitializeCriticalSection (&mono_delegate_section);
 
-       mono_thread_init (domain, start_cb, attach_cb);
+       mono_thread_init (start_cb, attach_cb);
        
        /* GC init has to happen after thread init */
        mono_gc_init ();
@@ -82,14 +82,15 @@ mono_runtime_init (MonoDomain *domain, MonoThreadStartCB start_cb,
        return;
 }
 
+/* This must not be called while there are still running threads executing
+ * managed code.
+ */
 void
 mono_runtime_cleanup (MonoDomain *domain)
 {
-       mono_thread_cleanup ();
-
-       /* Do this after the thread cleanup, because subthreads might
-        * still be doing socket calls.
-        */
+       /* Not really needed, but do it anyway */
+       mono_gc_cleanup ();
+       
        mono_network_cleanup ();
 }
 
index d27e31566d80d5b8c5bdef2bd9645a5b8dd353e6..e29e0c18db0676a480cb8b8504c047372f3c4ca6 100644 (file)
@@ -18,8 +18,9 @@
 #include <mono/utils/mono-hash.h>
 #include <mono/io-layer/io-layer.h>
 
-typedef void (*MonoThreadStartCB) (MonoThread *thread, gpointer stack_start, gpointer func);
-typedef void (*MonoThreadAttachCB) (MonoThread *thread, gpointer stack_start);
+typedef void (*MonoThreadStartCB) (guint32 tid, gpointer stack_start,
+                                  gpointer func);
+typedef void (*MonoThreadAttachCB) (guint32 tid, gpointer stack_start);
 
 /* This is a copy of System.AppDomainSetup */
 typedef struct {
diff --git a/mono/metadata/environment.c b/mono/metadata/environment.c
new file mode 100644 (file)
index 0000000..f8406bc
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * environment.c: System.Environment support internal calls
+ *
+ * Author:
+ *     Dick Porter (dick@ximian.com)
+ *
+ * (C) 2002 Ximian, Inc.
+ */
+
+#include <config.h>
+#include <glib.h>
+
+#include <mono/metadata/environment.h>
+
+static gint32 exitcode=0;
+
+gint32 mono_environment_exitcode_get (void)
+{
+       return(exitcode);
+}
+
+void mono_environment_exitcode_set (gint32 value)
+{
+       exitcode=value;
+}
diff --git a/mono/metadata/environment.h b/mono/metadata/environment.h
new file mode 100644 (file)
index 0000000..03ec4b5
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * environment.h: System.Environment support internal calls
+ *
+ * Author:
+ *     Dick Porter (dick@ximian.com)
+ *
+ * (C) 2002 Ximian, Inc
+ */
+
+#ifndef _MONO_METADATA_ENVIRONMENT_H_
+#define _MONO_METADATA_ENVIRONMENT_H_
+
+extern gint32 mono_environment_exitcode_get (void);
+extern void mono_environment_exitcode_set (gint32 value);
+
+#endif /* _MONO_METADATA_ENVIRONMENT_H_ */
index f63ddba2f7ab5ed5e0bfcea605781808db52196c..7eb7447e9b7824d6a8ec8822777735f7255d1888 100644 (file)
@@ -158,16 +158,39 @@ finalize_static_data (MonoClass *class, MonoVTable *vtable, GHashTable *todo) {
        finalize_fields (class, vtable->data, FALSE, todo);
 }
 
-void
-mono_domain_finalize (MonoDomain *domain) {
-
+static guint32
+internal_domain_finalize (gpointer data) {
+       MonoDomain *domain=(MonoDomain *)data;
        GHashTable *todo = g_hash_table_new (NULL, NULL);
+
+       mono_new_thread_init (GetCurrentThreadId (), todo, NULL);
+       
 #if HAVE_BOEHM_GC
        GC_gcollect ();
 #endif
        mono_g_hash_table_foreach (domain->class_vtable_hash, (GHFunc)finalize_static_data, todo);
        /* FIXME: finalize objects in todo... */
        g_hash_table_destroy (todo);
+
+       return(0);
+}
+
+void
+mono_domain_finalize (MonoDomain *domain) 
+{
+       HANDLE finalize_thread;
+       
+       /* Need to run managed code in a subthread.
+        * Mono_domain_finalize() is called from the main thread in
+        * the jit and the embedded example, hence the thread creation
+        * here.
+        */
+       finalize_thread=CreateThread (NULL, 0, internal_domain_finalize, domain, 0, NULL);
+       if(finalize_thread==NULL) {
+               g_assert_not_reached ();
+       }
+       WaitForSingleObject (finalize_thread, INFINITE);
+       CloseHandle (finalize_thread);
 }
 
 void
@@ -415,7 +438,7 @@ static guint32 finalizer_thread (gpointer unused)
 {
        guint32 stack_start;
        
-       mono_new_thread_init (NULL, &stack_start, NULL);
+       mono_new_thread_init (GetCurrentThreadId (), &stack_start, NULL);
        
        while(!finished) {
                /* Wait to be notified that there's at least one
@@ -438,7 +461,7 @@ static guint32 finalizer_thread (gpointer unused)
  * It's currently disabled because it still requires some
  * work in the rest of the runtime.
  */
-#undef ENABLE_FINALIZER_THREAD
+#define ENABLE_FINALIZER_THREAD
 
 void mono_gc_init (void)
 {
index 5edb71f57072da3b85147d7336e9011e83db4ccb..0ecc95b67e2f68a859a69cebc529525f89c2a703 100644 (file)
@@ -38,6 +38,7 @@
 #include <mono/metadata/string-icalls.h>
 #include <mono/metadata/debug-mono-symfile.h>
 #include <mono/metadata/process.h>
+#include <mono/metadata/environment.h>
 #include <mono/io-layer/io-layer.h>
 #include <mono/utils/strtod.h>
 
@@ -3517,6 +3518,8 @@ static gconstpointer icall_map [] = {
        "System.Environment::get_TickCount", ves_icall_System_Environment_get_TickCount,
        "System.Environment::Exit", ves_icall_System_Environment_Exit,
        "System.Environment::get_Platform", ves_icall_System_Environment_get_Platform,
+       "System.Environment::get_ExitCode", mono_environment_exitcode_get,
+       "System.Environment::set_ExitCode", mono_environment_exitcode_set,
 
        /*
         * System.Runtime.Remoting
index e2736066c7dbf1aa979f39ad800dda2a9afcb07e..5a9d071f37ed290946a2e189b0ca3948efd718d6 100644 (file)
@@ -24,6 +24,8 @@
 #include <mono/metadata/marshal.h>
 #include "mono/metadata/debug-helpers.h"
 #include "mono/metadata/marshal.h"
+#include <mono/metadata/threads.h>
+#include <mono/metadata/environment.h>
 #include "mono/metadata/profiler-private.h"
 #include <mono/os/gc_wrapper.h>
 
@@ -670,6 +672,25 @@ mono_unhandled_exception (MonoObject *exc)
        }
 }
 
+/*
+ * Launch a new thread to start all setup that requires managed code
+ * to be executed.
+ *
+ * main_func is called back from the thread with main_args as the
+ * parameter.  The callback function is expected to start Main()
+ * eventually.  This function then waits for all managed threads to
+ * finish.
+ */
+void
+mono_runtime_exec_managed_code (MonoDomain *domain,
+                               MonoMainThreadFunc main_func,
+                               gpointer main_args)
+{
+       mono_thread_create (domain, main_func, main_args);
+
+       mono_thread_manage ();
+}
+
 /*
  * Execute a standard Main() method (args doesn't contain the
  * executable name).
@@ -700,12 +721,21 @@ mono_runtime_exec_main (MonoMethod *method, MonoArray *args, MonoObject **exc)
                        rval = *(guint32 *)((char *)res + sizeof (MonoObject));
                else
                        rval = -1;
+
+               mono_environment_exitcode_set (rval);
        } else {
                mono_runtime_invoke (method, NULL, pa, exc);
                if (!exc || !*exc)
                        rval = 0;
-               else
+               else {
+                       /* If the return type of Main is void, only
+                        * set the exitcode if an exception was thrown
+                        * (we don't want to blow away an
+                        * explicitly-set exit code)
+                        */
                        rval = -1;
+                       mono_environment_exitcode_set (rval);
+               }
        }
 
        return rval;
index f35af0360f2167ad5d9a7a449f7330a1cd745b9c..bd1daad5bc9a3d3924e5d100d96889c6c8d4f2d8 100644 (file)
@@ -195,6 +195,7 @@ typedef struct {
 
 typedef MonoObject* (*MonoInvokeFunc)        (MonoMethod *method, void *obj, void **params, MonoObject **exc);
 typedef gpointer    (*MonoCompileFunc)       (MonoMethod *method);
+typedef void       (*MonoMainThreadFunc)    (gpointer user_data);
 
 #define mono_object_class(obj) (((MonoObject*)(obj))->vtable->klass)
 #define mono_object_domain(obj) (((MonoObject*)(obj))->vtable->domain)
@@ -323,6 +324,11 @@ mono_runtime_invoke_array   (MonoMethod *method, void *obj, MonoArray *params,
 MonoArray*
 mono_runtime_get_main_args  (void);
 
+void
+mono_runtime_exec_managed_code (MonoDomain *domain,
+                               MonoMainThreadFunc main_func,
+                               gpointer main_args);
+
 int
 mono_runtime_run_main       (MonoMethod *method, int argc, char* argv[], 
                             MonoObject **exc);
index 8ec21717225bb22c0e265365ef9ee32183065f96..0a3008732ac779bd9fdbf0cdd8441049f70dd9b1 100644 (file)
@@ -92,11 +92,8 @@ mono_thread_pool_add (MonoObject *target, MonoMethodMessage *msg, MonoDelegate *
        ReleaseSemaphore (mono_delegate_semaphore, 1, NULL);
 
        if (workers == 0) {
-               MonoThread *thread;
                workers++;
-               thread = mono_thread_create (domain, async_invoke_thread,
-                                            NULL);
-               g_assert (thread != NULL);
+               mono_thread_create (domain, async_invoke_thread, NULL);
        }
        LeaveCriticalSection (&mono_delegate_section);
 
index 79fc53c6826d2b215200683237074bcaef53a88d..bd0bf1c3e1a269bc9a01ec23cceccf9b0cceab04 100644 (file)
@@ -18,6 +18,7 @@
 #include <mono/metadata/threads.h>
 #include <mono/metadata/threads-types.h>
 #include <mono/metadata/exception.h>
+#include <mono/metadata/environment.h>
 #include <mono/io-layer/io-layer.h>
 
 #include <mono/os/gc_wrapper.h>
@@ -30,6 +31,7 @@ struct StartInfo
 {
        guint32 (*func)(void *);
        MonoThread *obj;
+       gboolean fake_thread;
        void *this;
        MonoDomain *domain;
 };
@@ -57,9 +59,6 @@ static CRITICAL_SECTION monitor_mutex;
  */
 static MonoGHashTable *threads=NULL;
 
-/* The MonoObject associated with the main thread */
-static MonoThread *main_thread;
-
 /* The TLS key that holds the MonoObject assigned to each thread */
 static guint32 current_object_key;
 
@@ -101,6 +100,7 @@ static void handle_store(MonoThread *thread)
         * the window in which the thread exists but we haven't
         * recorded it.
         */
+       mono_g_hash_table_remove (threads, &thread->tid);
        
        /* We don't need to duplicate thread->handle, because it is
         * only closed when the thread object is finalized by the GC.
@@ -148,30 +148,53 @@ static guint32 start_wrapper(void *data)
        guint32 (*start_func)(void *);
        void *this;
        guint32 tid;
+       MonoThread *thread;
        
 #ifdef THREAD_DEBUG
        g_message(G_GNUC_PRETTY_FUNCTION ": Start wrapper");
 #endif
        
-       start_func = start_info->func;
+       /* We can be sure start_info->obj->tid and
+        * start_info->obj->handle have been set, because the thread
+        * was created suspended, and these values were set before the
+        * thread resumed
+        */
+
+       tid=start_info->obj->tid;
+       
        mono_domain_set (start_info->domain);
-       this = start_info->this;
 
-       tid=GetCurrentThreadId ();
-       /* Set the thread ID here as well as in the parent thread,
-        * because we don't know whether the thread object will
-        * already have its ID set before we get to it.  This isn't a
-        * race condition, because if we're not guaranteed to get the
-        * same number in both the parent and child threads, then
-        * something else is seriously broken.
+       /* This MUST be called before any managed code can be
+        * executed, as it calls the callback function that (for the
+        * jit) sets the lmf marker.
         */
-       start_info->obj->tid=tid;
+       mono_new_thread_init (tid, &tid, start_func);
+
+       if(start_info->fake_thread) {
+               thread = (MonoThread *)mono_object_new (start_info->domain, mono_defaults.thread_class);
+
+               thread->handle=start_info->obj->handle;
+               thread->tid=tid;
+       } else {
+               thread=start_info->obj;
+       }
        
-       handle_store(start_info->obj);
+       start_func = start_info->func;
+       this = start_info->this;
 
-       mono_profiler_thread_start (tid);
+       TlsSetValue (current_object_key, thread);
 
-       mono_new_thread_init (start_info->obj, &tid, start_func);
+       handle_store(thread);
+
+       if(start_info->fake_thread) {
+               /* This has to happen _after_ handle_store(), because
+                * the fake thread is still in the threads hash until
+                * this call.
+                */
+               g_free (start_info->obj);
+       }
+
+       mono_profiler_thread_start (tid);
 
        g_free (start_info);
 
@@ -191,39 +214,39 @@ static guint32 start_wrapper(void *data)
        return(0);
 }
 
-void mono_new_thread_init (MonoThread *thread_object, gpointer stack_start, gpointer func)
+void mono_new_thread_init (guint32 tid, gpointer stack_start, gpointer func)
 {
-       /* FIXME: GC problem here with recorded object
-        * pointer!
-        *
-        * This is recorded so CurrentThread can return the
-        * Thread object.
-        */
-       TlsSetValue (current_object_key, thread_object);
-
        if (mono_thread_start_cb) {
-               mono_thread_start_cb (thread_object, stack_start, func);
+               mono_thread_start_cb (tid, stack_start, func);
        }
 }
 
-MonoThread *mono_thread_create (MonoDomain *domain, gpointer func,
-                               gpointer arg)
+void mono_thread_create (MonoDomain *domain, gpointer func, gpointer arg)
 {
        MonoThread *thread;
        HANDLE thread_handle;
        struct StartInfo *start_info;
        guint32 tid;
        
-       thread = (MonoThread *)mono_object_new (domain,
-                                               mono_defaults.thread_class);
+       /* This is just a temporary allocation.  The object will be
+        * created properly with mono_object_new() inside
+        * start_wrapper().  (This is so the main thread can be
+        * created without needing to run any managed code.)
+        */
+       thread=g_new0 (MonoThread, 1);
 
        start_info=g_new0 (struct StartInfo, 1);
        start_info->func = func;
        start_info->obj = thread;
+       start_info->fake_thread = TRUE;
        start_info->domain = domain;
        start_info->this = arg;
                
-       thread_handle = CreateThread(NULL, 0, start_wrapper, start_info, 0, &tid);
+       /* Create suspended, so we can do some housekeeping before the thread
+        * starts
+        */
+       thread_handle = CreateThread(NULL, 0, start_wrapper, start_info,
+                                    CREATE_SUSPENDED, &tid);
 #ifdef THREAD_DEBUG
        g_message(G_GNUC_PRETTY_FUNCTION ": Started thread ID %d (handle %p)",
                  tid, thread_handle);
@@ -235,7 +258,7 @@ MonoThread *mono_thread_create (MonoDomain *domain, gpointer func,
 
        handle_store(thread);
 
-       return thread;
+       ResumeThread (thread_handle);
 }
 
 MonoThread *
@@ -248,7 +271,7 @@ mono_thread_attach (MonoDomain *domain)
        if ((thread = mono_thread_current ())) {
                g_warning ("mono_thread_attach called for an already attached thread");
                if (mono_thread_attach_cb) {
-                       mono_thread_attach_cb (thread, &tid);
+                       mono_thread_attach_cb (tid, &tid);
                }
                return thread;
        }
@@ -275,7 +298,7 @@ mono_thread_attach (MonoDomain *domain)
        mono_domain_set (domain);
 
        if (mono_thread_attach_cb) {
-               mono_thread_attach_cb (thread, &tid);
+               mono_thread_attach_cb (tid, &tid);
        }
 
        return(thread);
@@ -315,6 +338,7 @@ HANDLE ves_icall_System_Threading_Thread_Thread_internal(MonoThread *this,
                start_info->func = start_func;
                start_info->this = delegate;
                start_info->obj = this;
+               start_info->fake_thread = FALSE;
                start_info->domain = mono_domain_get ();
 
                thread=CreateThread(NULL, 0, start_wrapper, start_info,
@@ -695,18 +719,31 @@ void ves_icall_System_Threading_Monitor_Monitor_pulse(MonoObject *obj)
        MONO_ARCH_SAVE_REGS;
 
 #ifdef THREAD_LOCK_DEBUG
-       g_message("(%d) Pulsing %p", GetCurrentThreadId (), obj);
+       g_message(G_GNUC_PRETTY_FUNCTION "(%d) Pulsing %p",
+                 GetCurrentThreadId (), obj);
 #endif
 
        EnterCriticalSection(&monitor_mutex);
        
        mon=obj->synchronisation;
        if(mon==NULL) {
+#ifdef THREAD_LOCK_DEBUG
+               g_message (G_GNUC_PRETTY_FUNCTION
+                          "(%d) object %p not locked", GetCurrentThreadId (),
+                          obj);
+#endif
+
                LeaveCriticalSection(&monitor_mutex);
                return;
        }
 
        if(mon->tid!=GetCurrentThreadId()) {
+#ifdef THREAD_LOCK_DEBUG
+               g_message (G_GNUC_PRETTY_FUNCTION
+                          "(%d) doesn't own lock (owned by %d)",
+                          GetCurrentThreadId (), mon->tid);
+#endif
+
                LeaveCriticalSection(&monitor_mutex);
                return;
        }
@@ -774,7 +811,8 @@ gboolean ves_icall_System_Threading_Monitor_Monitor_wait(MonoObject *obj,
        MONO_ARCH_SAVE_REGS;
 
 #ifdef THREAD_LOCK_DEBUG
-       g_message("(%d) Trying to wait for %p with timeout %dms",
+       g_message(G_GNUC_PRETTY_FUNCTION
+                 "(%d) Trying to wait for %p with timeout %dms",
                  GetCurrentThreadId (), obj, ms);
 #endif
 
@@ -1193,29 +1231,9 @@ ves_icall_System_Threading_Thread_ResetAbort (void)
        }
 }
 
-void mono_thread_init (MonoDomain *domain, MonoThreadStartCB start_cb, MonoThreadAttachCB attach_cb)
+void mono_thread_init (MonoThreadStartCB start_cb,
+                      MonoThreadAttachCB attach_cb)
 {
-       /* Build a System.Threading.Thread object instance to return
-        * for the main line's Thread.CurrentThread property.
-        */
-       
-       /* I wonder what happens if someone tries to destroy this
-        * object? In theory, I guess the whole program should act as
-        * though exit() were called :-)
-        */
-#ifdef THREAD_DEBUG
-       g_message(G_GNUC_PRETTY_FUNCTION
-                 ": Starting to build main Thread object");
-#endif
-       main_thread = (MonoThread *)mono_object_new (domain, mono_defaults.thread_class);
-
-       main_thread->handle = GetCurrentThread ();
-
-#ifdef THREAD_DEBUG
-       g_message(G_GNUC_PRETTY_FUNCTION
-                 ": Finished building main Thread object: %p", main_thread);
-#endif
-
        InitializeCriticalSection(&threads_mutex);
        InitializeCriticalSection(&monitor_mutex);
        InitializeCriticalSection(&interlocked_mutex);
@@ -1226,8 +1244,6 @@ void mono_thread_init (MonoDomain *domain, MonoThreadStartCB start_cb, MonoThrea
                   current_object_key);
 #endif
 
-       TlsSetValue(current_object_key, main_thread);
-
        mono_thread_start_cb = start_cb;
        mono_thread_attach_cb = attach_cb;
 
@@ -1327,7 +1343,7 @@ static void build_wait_tids (gpointer key, gpointer value, gpointer user)
        }
 }
 
-void mono_thread_cleanup(void)
+void mono_thread_manage (void)
 {
        struct wait_data *wait=g_new0 (struct wait_data, 1);
        
@@ -1367,3 +1383,30 @@ void mono_thread_cleanup(void)
        mono_g_hash_table_destroy(threads);
        threads=NULL;
 }
+
+static void terminate_thread (gpointer key, gpointer value, gpointer user)
+{
+       MonoThread *thread=(MonoThread *)value;
+       guint32 self=GPOINTER_TO_UINT (user);
+       
+       if(thread->tid!=self) {
+               /*TerminateThread (thread->handle, -1);*/
+       }
+}
+
+void mono_thread_abort_all_other_threads (void)
+{
+       guint32 self=GetCurrentThreadId ();
+
+       EnterCriticalSection (&threads_mutex);
+#ifdef THREAD_DEBUG
+       g_message(G_GNUC_PRETTY_FUNCTION ":There are %d threads to abort",
+                 mono_g_hash_table_size (threads));
+       mono_g_hash_table_foreach (threads, print_tids, NULL);
+#endif
+
+       mono_g_hash_table_foreach (threads, terminate_thread,
+                                  GUINT_TO_POINTER (self));
+       
+       LeaveCriticalSection (&threads_mutex);
+}
index 26d5b07302c4582f475c2c7159e9f83f7237b151..b4445fda1460a97fedca205d78ba148798b19ae5 100644 (file)
 
 extern int  mono_thread_get_abort_signal (void);
 
-extern void mono_thread_init (MonoDomain *domain, MonoThreadStartCB start_cb,
+extern void mono_thread_init (MonoThreadStartCB start_cb,
                              MonoThreadAttachCB attach_cb);
-extern void mono_thread_cleanup(void);
+extern void mono_thread_manage(void);
+extern void mono_thread_abort_all_other_threads (void);
+
 extern MonoThread *mono_thread_current (void);
 
 typedef struct {
@@ -31,10 +33,9 @@ typedef struct {
 
 extern void mono_install_thread_callbacks (MonoThreadCallbacks *callbacks);
 
-extern void mono_new_thread_init (MonoThread *thread_object,
-                                 gpointer stack_start,
+extern void mono_new_thread_init (guint32 tid, gpointer stack_start,
                                  gpointer func);
-extern MonoThread *mono_thread_create (MonoDomain *domain, gpointer func,
+extern void mono_thread_create (MonoDomain *domain, gpointer func,
                                       gpointer arg);
 extern MonoThread *mono_thread_attach (MonoDomain *domain);
 
index ee7b84227d7990428ca328ee1508f5bb8ca317a9..eb7131508c6972870246938ca02a1219da041914 100644 (file)
@@ -1,4 +1,5 @@
 #include <mono/jit/jit.h>
+#include <mono/metadata/environment.h>
 
 /*
  * Very simple mono embedding example.
@@ -14,13 +15,40 @@ gimme () {
        return mono_string_new (mono_domain_get (), "All your monos are belong to us!");
 }
 
+typedef struct
+{
+       MonoDomain *domain;
+       const char *file;
+       int argc;
+       char **argv;
+} MainThreadArgs;
+
+static void main_thread_handler (gpointer user_data)
+{
+       MainThreadArgs *main_args=(MainThreadArgs *)user_data;
+       MonoAssembly *assembly;
+
+       assembly = mono_domain_assembly_open (main_args->domain,
+                                             main_args->file);
+       if (!assembly)
+               exit (2);
+       /*
+        * mono_jit_exec() will run the Main() method in the assembly.
+        * The return value needs to be looked up from
+        * System.Environment.ExitCode.
+        */
+       mono_jit_exec (main_args->domain, assembly, main_args->argc,
+                      main_args->argv);
+}
+
+
 int 
 main(int argc, char* argv[]) {
        MonoDomain *domain;
-       MonoAssembly *assembly;
        const char *file;
        int retval;
-
+       MainThreadArgs main_args;
+       
        if (argc < 2){
                fprintf (stderr, "Please provide an assembly to load");
                return 1;
@@ -36,14 +64,17 @@ main(int argc, char* argv[]) {
         * can call us back.
         */
        mono_add_internal_call ("Mono::gimme", gimme);
-       assembly = mono_domain_assembly_open (domain, file);
-       if (!assembly)
-               return 2;
-       /*
-        * mono_jit_exec() will run the Main() method in the assembly
-        * and return the value.
-        */
-       retval = mono_jit_exec (domain, assembly, argc - 1, argv + 1);
+
+       main_args.domain=domain;
+       main_args.file=file;
+       main_args.argc=argc-1;
+       main_args.argv=argv+1;
+       
+       mono_runtime_exec_managed_code (domain, main_thread_handler,
+                                       &main_args);
+       
+       retval=mono_environment_exitcode_get ();
+       
        mono_jit_cleanup (domain);
        return retval;
 }