X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;ds=sidebyside;f=mono%2Fmetadata%2Fappdomain.c;h=80e36207fca72b4966be7dc13211de6a2fe1a623;hb=ccf4e0f3ad38270a2b31ec5df9569bcc8c1ca2b4;hp=03d4d3ae77a305ab423f8536952312e736e8fe2b;hpb=f1085634cb9086604a22f991c04ef3a93c6036ef;p=mono.git diff --git a/mono/metadata/appdomain.c b/mono/metadata/appdomain.c index 03d4d3ae77a..80e36207fca 100644 --- a/mono/metadata/appdomain.c +++ b/mono/metadata/appdomain.c @@ -23,13 +23,18 @@ #include #include #include +#include +#include + +#define MONO_CORLIB_VERSION 20 -HANDLE mono_delegate_semaphore = NULL; CRITICAL_SECTION mono_delegate_section; static gunichar2 process_guid [36]; static gboolean process_guid_set = FALSE; +static gboolean shutting_down = FALSE; + static MonoAssembly * mono_domain_assembly_preload (MonoAssemblyName *aname, gchar **assemblies_path, @@ -41,6 +46,9 @@ mono_domain_fire_assembly_load (MonoAssembly *assembly, gpointer user_data); static MonoMethod * look_for_method_by_name (MonoClass *klass, const gchar *name); +static void +mono_domain_unload (MonoDomain *domain); + /* * mono_runtime_init: * @domain: domain returned by mono_init () @@ -60,11 +68,16 @@ mono_runtime_init (MonoDomain *domain, MonoThreadStartCB start_cb, MonoAppDomainSetup *setup; MonoAppDomain *ad; MonoClass *class; + MonoString *arg; + + mono_marshal_init (); mono_install_assembly_preload_hook (mono_domain_assembly_preload, NULL); mono_install_assembly_load_hook (mono_domain_fire_assembly_load, NULL); mono_install_lookup_dynamic_token (mono_reflection_lookup_dynamic_token); + mono_thread_init (start_cb, attach_cb); + class = mono_class_from_name (mono_defaults.corlib, "System", "AppDomainSetup"); setup = (MonoAppDomainSetup *) mono_object_new (domain, class); @@ -74,14 +87,28 @@ mono_runtime_init (MonoDomain *domain, MonoThreadStartCB start_cb, domain->domain = ad; domain->setup = setup; - mono_delegate_semaphore = CreateSemaphore (NULL, 0, 0x7fffffff, NULL); - g_assert (mono_delegate_semaphore != INVALID_HANDLE_VALUE); InitializeCriticalSection (&mono_delegate_section); mono_context_init (domain); mono_context_set (domain->default_context); - mono_thread_init (start_cb, attach_cb); + mono_type_initialization_init (); + + + /* + * Create an instance early since we can't do it when there is no memory. + */ + arg = mono_string_new (domain, "Out of memory"); + domain->out_of_memory_ex = mono_exception_from_name_two_strings (mono_defaults.corlib, "System", "OutOfMemoryException", arg, NULL); + + /* + * These two are needed because the signal handlers might be executing on + * an alternate stack, and Boehm GC can't handle that. + */ + arg = mono_string_new (domain, "A null value was found where an object instance was required"); + domain->null_reference_ex = mono_exception_from_name_two_strings (mono_defaults.corlib, "System", "NullReferenceException", arg, NULL); + arg = mono_string_new (domain, "The requested operation caused a stack overflow."); + domain->stack_overflow_ex = mono_exception_from_name_two_strings (mono_defaults.corlib, "System", "StackOverflowException", arg, NULL); /* GC init has to happen after thread init */ mono_gc_init (); @@ -91,6 +118,34 @@ mono_runtime_init (MonoDomain *domain, MonoThreadStartCB start_cb, return; } +static int +mono_get_corlib_version (void) +{ + MonoClass *klass; + MonoClassField *field; + MonoObject *value; + + klass = mono_class_from_name (mono_defaults.corlib, "System", "Environment"); + mono_class_init (klass); + field = mono_class_get_field_from_name (klass, "mono_corlib_version"); + if (!field) + return -1; + if (! (field->type->attrs & FIELD_ATTRIBUTE_STATIC)) + return -1; + value = mono_field_get_value_object (mono_domain_get (), field, NULL); + return *(gint32*)((gchar*)value + sizeof (MonoObject)); +} + +const char* +mono_check_corlib_version (void) +{ + int version = mono_get_corlib_version (); + if (version != MONO_CORLIB_VERSION) + return g_strdup_printf ("expected corlib version %d, found %d.", MONO_CORLIB_VERSION, version); + else + return NULL; +} + void mono_context_init (MonoDomain *domain) { @@ -110,9 +165,11 @@ mono_context_init (MonoDomain *domain) void mono_runtime_cleanup (MonoDomain *domain) { + shutting_down = TRUE; + /* This ends up calling any pending pending (for at most 2 seconds) */ mono_gc_cleanup (); - + mono_network_cleanup (); } @@ -131,6 +188,12 @@ mono_runtime_quit () quit_function (mono_root_domain, NULL); } +gboolean +mono_runtime_is_shutting_down (void) +{ + return shutting_down; +} + gboolean mono_domain_has_type_resolve (MonoDomain *domain) { @@ -178,30 +241,24 @@ mono_domain_try_type_resolve (MonoDomain *domain, char *name, MonoObject *tb) return (MonoReflectionAssembly *) mono_runtime_invoke (method, domain->domain, params, NULL); } -void -ves_icall_System_AppDomainSetup_InitAppDomainSetup (MonoAppDomainSetup *setup) +/* + * mono_domain_set: + * + * Set the current appdomain to @domain. If @force is set, set it even + * if it is being unloaded. + * Returns: + * - TRUE on success + * - FALSE if the domain is being unloaded + */ +gboolean +mono_domain_set (MonoDomain *domain, gboolean force) { - MonoDomain* domain = mono_domain_get (); - MonoAssembly *assembly; - gchar *str; - gchar *config_suffix; - - MONO_ARCH_SAVE_REGS; - - if (!domain->entry_assembly) - assembly = mono_root_domain->entry_assembly; - else - assembly = domain->entry_assembly; - - g_assert (assembly); + if (!force && (domain->state == MONO_APPDOMAIN_UNLOADING || domain->state == MONO_APPDOMAIN_UNLOADED)) + return FALSE; - setup->application_base = mono_string_new (domain, assembly->basedir); + mono_domain_set_internal (domain); - config_suffix = g_strconcat (assembly->aname.name, ".exe.config", NULL); - str = g_build_filename (assembly->basedir, config_suffix, NULL); - g_free (config_suffix); - setup->configuration_file = mono_string_new (domain, str); - g_free (str); + return TRUE; } MonoObject * @@ -319,6 +376,7 @@ ves_icall_System_AppDomain_createDomain (MonoString *friendly_name, MonoAppDomai data->domain = ad; data->setup = setup; data->friendly_name = mono_string_to_utf8 (friendly_name); + data->out_of_memory_ex = mono_exception_from_name_domain (data, mono_defaults.corlib, "System", "OutOfMemoryException"); mono_context_init (data); @@ -413,15 +471,22 @@ add_assemblies_to_domain (MonoDomain *domain, MonoAssembly *ass) { gint i; - if (g_hash_table_lookup (domain->assemblies, ass->aname.name)) - return; /* This is ok while no lazy loading of assemblies */ + if (!ass->aname.name) + return; mono_domain_lock (domain); + + if (g_hash_table_lookup (domain->assemblies, ass->aname.name)) { + mono_domain_unlock (domain); + return; /* This is ok while no lazy loading of assemblies */ + } + g_hash_table_insert (domain->assemblies, (gpointer) ass->aname.name, ass); mono_domain_unlock (domain); - for (i = 0; ass->image->references [i] != NULL; i++) - add_assemblies_to_domain (domain, ass->image->references [i]); + if (ass->image->references) + for (i = 0; ass->image->references [i] != NULL; i++) + add_assemblies_to_domain (domain, ass->image->references [i]); } static void @@ -451,6 +516,48 @@ mono_domain_fire_assembly_load (MonoAssembly *assembly, gpointer user_data) mono_runtime_invoke (method, domain->domain, params, NULL); } +static gchar * +reduce_path (const gchar *dirname) +{ + gchar **parts; + gchar *part; + GList *list, *tmp; + GString *result; + gchar *res; + gint i; + + parts = g_strsplit (dirname, G_DIR_SEPARATOR_S, 0); + list = NULL; + for (i = 0; (part = parts [i]) != NULL; i++) { + if (!strcmp (part, ".")) + continue; + + if (!strcmp (part, "..")) { + if (list && list->next) /* Don't remove root */ + list = g_list_delete_link (list, list); + } else { + list = g_list_prepend (list, part); + } + } + + result = g_string_new (""); + list = g_list_reverse (list); + + for (tmp = list; tmp; tmp = tmp->next) { + gchar *data = (gchar *) tmp->data; + + if (data && *data) + g_string_append_printf (result, "%c%s", G_DIR_SEPARATOR, + (char *) tmp->data); + } + + res = result->str; + g_string_free (result, FALSE); + g_list_free (list); + g_strfreev (parts); + return res; +} + static void set_domain_search_path (MonoDomain *domain) { @@ -461,14 +568,16 @@ set_domain_search_path (MonoDomain *domain) gint npaths = 0; gchar **pvt_split = NULL; GError *error = NULL; + gint appbaselen = -1; if ((domain->search_path != NULL) && !domain->setup->path_changed) return; setup = domain->setup; - if (setup->application_base) - npaths++; + if (!setup->application_base) + return; /* Must set application base to get private path working */ + npaths++; if (setup->private_bin_path) { utf8 = mono_string_to_utf8 (setup->private_bin_path); pvt_split = g_strsplit (utf8, G_SEARCHPATH_SEPARATOR_S, 1000); @@ -494,44 +603,59 @@ set_domain_search_path (MonoDomain *domain) domain->search_path = tmp = g_malloc ((npaths + 1) * sizeof (gchar *)); tmp [npaths] = NULL; - if (setup->application_base) { - *tmp = mono_string_to_utf8 (setup->application_base); - - /* FIXME: is this needed? */ - if (strncmp (*tmp, "file://", 7) == 0) { - gchar *file = *tmp; - gchar *uri = *tmp; - - if (uri [7] != '/') - uri = g_strdup_printf ("file:///%s", uri + 7); - - *tmp = g_filename_from_uri (uri, NULL, &error); - if (uri != file) - g_free (uri); - - if (error != NULL) { - g_warning ("%s\n", error->message); - g_error_free (error); - *tmp = file; - } else { - g_free (file); - } + + *tmp = mono_string_to_utf8 (setup->application_base); + + /* FIXME: is this needed? */ + if (strncmp (*tmp, "file://", 7) == 0) { + gchar *file = *tmp; + gchar *uri = *tmp; + gchar *tmpuri; + + if (uri [7] != '/') + uri = g_strdup_printf ("file:///%s", uri + 7); + + tmpuri = uri; + uri = mono_escape_uri_string (tmpuri); + *tmp = g_filename_from_uri (uri, NULL, &error); + g_free (uri); + + if (tmpuri != file) + g_free (tmpuri); + + if (error != NULL) { + g_warning ("%s\n", error->message); + g_error_free (error); + *tmp = file; + } else { + g_free (file); } - - } else { - *tmp = g_strdup (""); } for (i = 1; pvt_split && i < npaths; i++) { - if (*tmp [0] == '\0' || g_path_is_absolute (pvt_split [i - 1])) { - tmp [i] = g_strdup (pvt_split [i - 1]); - continue; - } - tmp [i] = g_build_filename (tmp [0], pvt_split [i - 1], NULL); + if (strchr (tmp [i], '.')) { + gchar *reduced; + gchar *freeme; + + reduced = reduce_path (tmp [i]); + if (appbaselen == -1) + appbaselen = strlen (tmp [0]); + + if (strncmp (tmp [0], reduced, appbaselen)) { + g_free (reduced); + g_free (tmp [i]); + tmp [i] = g_strdup (""); + continue; + } + + freeme = tmp [i]; + tmp [i] = reduced; + g_free (freeme); + } } - if (setup->private_bin_path_probe != NULL && setup->application_base) { + if (setup->private_bin_path_probe != NULL) { g_free (tmp [0]); tmp [0] = g_strdup (""); } @@ -541,24 +665,67 @@ set_domain_search_path (MonoDomain *domain) g_strfreev (pvt_split); } +static gboolean +try_load_from (MonoAssembly **assembly, const gchar *path1, const gchar *path2, + const gchar *path3, const gchar *path4) +{ + gchar *fullpath; + + *assembly = NULL; + fullpath = g_build_filename (path1, path2, path3, path4, NULL); + if (g_file_test (fullpath, G_FILE_TEST_IS_REGULAR)) + *assembly = mono_assembly_open (fullpath, NULL); + + g_free (fullpath); + return (*assembly != NULL); +} + static MonoAssembly * -real_load (gchar **search_path, gchar *filename) +real_load (gchar **search_path, const gchar *culture, const gchar *name) { - MonoAssembly *result; + MonoAssembly *result = NULL; gchar **path; - gchar *fullpath; + gchar *filename; + const gchar *local_culture; + gint len; + + if (!culture || *culture == '\0') { + local_culture = ""; + } else { + local_culture = culture; + } + + filename = g_strconcat (name, ".dll", NULL); + len = strlen (filename); for (path = search_path; *path; path++) { if (**path == '\0') continue; /* Ignore empty ApplicationBase */ - fullpath = g_build_filename (*path, filename, NULL); - result = mono_assembly_open (fullpath, NULL); - g_free (fullpath); - if (result) - return result; + + /* See test cases in bug #58992 and bug #57710 */ + /* 1st try: [culture]/[name].dll (culture may be empty) */ + strcpy (filename + len - 4, ".dll"); + if (try_load_from (&result, *path, local_culture, "", filename)) + break; + + /* 2nd try: [culture]/[name].exe (culture may be empty) */ + strcpy (filename + len - 4, ".exe"); + if (try_load_from (&result, *path, local_culture, "", filename)) + break; + + /* 3rd try: [culture]/[name]/[name].dll (culture may be empty) */ + strcpy (filename + len - 4, ".dll"); + if (try_load_from (&result, *path, local_culture, name, filename)) + break; + + /* 4th try: [culture]/[name]/[name].exe (culture may be empty) */ + strcpy (filename + len - 4, ".exe"); + if (try_load_from (&result, *path, local_culture, name, filename)) + break; } - return NULL; + g_free (filename); + return result; } /* @@ -571,50 +738,18 @@ mono_domain_assembly_preload (MonoAssemblyName *aname, gpointer user_data) { MonoDomain *domain = mono_domain_get (); - MonoAssembly *result; - gchar *dll, *exe; + MonoAssembly *result = NULL; set_domain_search_path (domain); - dll = g_strconcat (aname->name, ".dll", NULL); - exe = g_strdup (dll); - strcpy (exe + strlen (exe) - 4, ".exe"); - if (domain->search_path && domain->search_path [0] != NULL) { - /* TODO: should also search in name/name.dll and name/name.exe from appbase */ - result = real_load (domain->search_path, dll); - if (result) { - g_free (dll); - g_free (exe); - return result; - } - - result = real_load (domain->search_path, exe); - if (result) { - g_free (dll); - g_free (exe); - return result; - } + result = real_load (domain->search_path, aname->culture, aname->name); } - if (assemblies_path && assemblies_path [0] != NULL) { - result = real_load (assemblies_path, dll); - if (result) { - g_free (dll); - g_free (exe); - return result; - } - - result = real_load (assemblies_path, exe); - if (result) { - g_free (dll); - g_free (exe); - return result; - } + if (result == NULL && assemblies_path && assemblies_path [0] != NULL) { + result = real_load (assemblies_path, aname->culture, aname->name); } - - g_free (dll); - g_free (exe); + return NULL; } @@ -659,7 +794,7 @@ free_assembly_name (MonoAssemblyName *aname) } static gboolean -get_info_from_assembly_name (MonoReflectionAssemblyName *assRef, MonoAssemblyName *aname) +get_info_from_assembly_name (MonoString *assRef, MonoAssemblyName *aname) { gchar *name; gchar *value; @@ -669,7 +804,7 @@ get_info_from_assembly_name (MonoReflectionAssemblyName *assRef, MonoAssemblyNam memset (aname, 0, sizeof (MonoAssemblyName)); - name = mono_string_to_utf8 (assRef->name); + name = mono_string_to_utf8 (assRef); parts = tmp = g_strsplit (name, ",", 4); g_free (name); if (!tmp || !*tmp) { @@ -686,103 +821,88 @@ get_info_from_assembly_name (MonoReflectionAssemblyName *assRef, MonoAssemblyNam return TRUE; } - value = g_strstrip (*tmp); - if (strncmp (value, "Version=", 8)) { - g_strfreev (parts); - return FALSE; - } - - if (sscanf (value + 8, "%u.%u.%u.%u", &major, &minor, &build, &revision) != 4) { - g_strfreev (parts); - return FALSE; - } + while (*tmp) { + value = g_strstrip (*tmp); + if (!g_ascii_strncasecmp (value, "Version=", 8)) { + if (sscanf (value + 8, "%u.%u.%u.%u", + &major, &minor, &build, &revision) != 4) { + g_strfreev (parts); + return FALSE; + } + /* g_print ("Version: %u.%u.%u.%u\n", major, minor, build, revision); */ + aname->major = major; + aname->minor = minor; + aname->build = build; + aname->revision = revision; + tmp++; + continue; + } - /* g_print ("Version: %u.%u.%u.%u\n", major, minor, build, revision); */ - aname->major = major; - aname->minor = minor; - aname->build = build; - aname->revision = revision; - tmp++; + if (!g_ascii_strncasecmp (value, "Culture=", 8)) { + gchar *t = g_strdup (value + 8); + g_strchug (t); + aname->culture = g_strdup (g_strchomp (t)); + tmp++; + g_free (t); + if (g_strcasecmp (aname->culture, "neutral") == 0) { + g_free ((void *) aname->culture); + aname->culture = g_strdup (""); + } + continue; + } - if (!*tmp) { - g_strfreev (parts); - return FALSE; - } + if (!g_ascii_strncasecmp (value, "PublicKeyToken=", 15)) { + tmp++; + value += 15; + if (*value && strncmp (value, "null", 4)) { + gchar *t = g_strdup (value); + g_strchug (t); + aname->public_tok_value = g_strdup (g_strchomp (value)); + g_free (t); + } + continue; + } - value = g_strstrip (*tmp); - if (strncmp (value, "Culture=", 8)) { g_strfreev (parts); return FALSE; } - /* g_print ("Culture: %s\n", aname->culture); */ - aname->culture = g_strstrip (g_strdup (value + 8)); - tmp++; - - if (!*tmp) { - g_strfreev (parts); - return FALSE; - } + g_strfreev (parts); + return TRUE; +} - value = g_strstrip (*tmp); - if (strncmp (value, "PublicKeyToken=", 15)) { - g_strfreev (parts); - return FALSE; +MonoReflectionAssembly * +ves_icall_System_AppDomain_LoadAssemblyRaw (MonoAppDomain *ad, + MonoArray *raw_assembly, + MonoArray *raw_symbol_store, MonoObject *evidence) +{ + MonoAssembly *ass; + MonoDomain *domain = ad->data; + MonoImageOpenStatus status; + guint32 raw_assembly_len = mono_array_length (raw_assembly); + MonoImage *image = mono_image_open_from_data (mono_array_addr (raw_assembly, gchar, 0), raw_assembly_len, TRUE, NULL); + + if (raw_symbol_store) + mono_raise_exception (mono_get_exception_not_implemented ("LoadAssemblyRaw: Raw Symbol Store not Implemented")); + + if (!image) { + mono_raise_exception (mono_get_exception_bad_image_format ("")); + return NULL; } - value += 15; - if (*value && strcmp (value, "null")) { - gint i, len; - gchar h, l; - gchar *result; - - value = g_strstrip (g_strdup (value)); - len = strlen (value); - if (len % 2) { - g_free (value); - g_strfreev (parts); - return FALSE; - } - - aname->hash_len = len / 2; - aname->hash_value = g_malloc0 (aname->hash_len); - result = (gchar *) aname->hash_value; - - for (i = 0; i < len; i++) { - if (i % 2) { - l = g_ascii_xdigit_value (value [i]); - if (l == -1) { - g_free (value); - g_strfreev (parts); - return FALSE; - } - result [i / 2] = (h * 16) + l; - } else { - h = g_ascii_xdigit_value (value [i]); - if (h == -1) { - g_free (value); - g_strfreev (parts); - return FALSE; - } - } - } - g_free (value); + ass = mono_assembly_load_from (image, "", &status); - /* - g_print ("PublicKeyToken: "); - for (i = 0; i < aname->hash_len; i++) { - g_print ("%x", 0x00FF & aname->hash_value [i]); - } - g_print ("\n"); - */ + if (!ass) { + mono_image_close (image); + mono_raise_exception (mono_get_exception_bad_image_format ("")); + return NULL; } - g_strfreev (parts); - return TRUE; + return mono_assembly_get_object (domain, ass); } MonoReflectionAssembly * -ves_icall_System_AppDomain_LoadAssembly (MonoAppDomain *ad, MonoReflectionAssemblyName *assRef, MonoObject *evidence) +ves_icall_System_AppDomain_LoadAssembly (MonoAppDomain *ad, MonoString *assRef, MonoObject *evidence) { MonoDomain *domain = ad->data; MonoImageOpenStatus status = MONO_IMAGE_OK; @@ -792,28 +912,25 @@ ves_icall_System_AppDomain_LoadAssembly (MonoAppDomain *ad, MonoReflectionAssem MONO_ARCH_SAVE_REGS; - memset (&aname, 0, sizeof (aname)); - /* FIXME : examine evidence? */ g_assert (assRef != NULL); - g_assert (assRef->name != NULL); if (!get_info_from_assembly_name (assRef, &aname)) { MonoException *exc; free_assembly_name (&aname); /* This is a parse error... */ - exc = mono_get_exception_file_not_found (assRef->name); + exc = mono_get_exception_file_not_found (assRef); mono_raise_exception (exc); } ass = mono_assembly_load (&aname, NULL, &status); free_assembly_name (&aname); - if (!ass && (refass = try_assembly_resolve (domain, assRef->name)) == NULL){ + if (!ass && (refass = try_assembly_resolve (domain, assRef)) == NULL){ /* FIXME: it doesn't make much sense since we really don't have a filename ... */ - MonoException *exc = mono_get_exception_file_not_found (assRef->name); + MonoException *exc = mono_get_exception_file_not_found (assRef); mono_raise_exception (exc); } @@ -835,8 +952,30 @@ ves_icall_System_AppDomain_InternalUnload (gint32 domain_id) mono_raise_exception (exc); } + if (domain == mono_root_domain) { + mono_raise_exception (mono_get_exception_cannot_unload_appdomain ("The default appdomain can not be unloaded.")); + return; + } - mono_domain_unload (domain, FALSE); + /* + * Unloading seems to cause problems when running NUnit/NAnt, hence + * this workaround. + */ + if (g_getenv ("MONO_NO_UNLOAD")) + return; + + mono_domain_unload (domain); +} + +gboolean +ves_icall_System_AppDomain_InternalIsFinalizingForUnload (gint32 domain_id) +{ + MonoDomain *domain = mono_domain_get_by_id (domain_id); + + if (!domain) + return TRUE; + + return mono_domain_is_unloading (domain); } gint32 @@ -857,7 +996,7 @@ ves_icall_System_AppDomain_ExecuteAssembly (MonoAppDomain *ad, MonoString *file, if (!assembly) { mono_raise_exception ((MonoException *)mono_exception_from_name ( - mono_defaults.corlib, "System.IO", "FileNotFoundException")); + mono_defaults.corlib, "System.IO", "FileNotFoundException")); } image = assembly->image; @@ -890,7 +1029,8 @@ ves_icall_System_AppDomain_InternalSetDomain (MonoAppDomain *ad) MONO_ARCH_SAVE_REGS; - mono_domain_set(ad->data); + if (!mono_domain_set (ad->data, FALSE)) + mono_raise_exception (mono_get_exception_appdomain_unloaded ()); return old_domain->domain; } @@ -903,11 +1043,45 @@ ves_icall_System_AppDomain_InternalSetDomainByID (gint32 domainid) MONO_ARCH_SAVE_REGS; - mono_domain_set (domain); - + if (!domain || !mono_domain_set (domain, FALSE)) + mono_raise_exception (mono_get_exception_appdomain_unloaded ()); + return current_domain->domain; } +void +ves_icall_System_AppDomain_InternalPushDomainRef (MonoAppDomain *ad) +{ + MONO_ARCH_SAVE_REGS; + + mono_thread_push_appdomain_ref (ad->data); +} + +void +ves_icall_System_AppDomain_InternalPushDomainRefByID (gint32 domain_id) +{ + MonoDomain *domain = mono_domain_get_by_id (domain_id); + + MONO_ARCH_SAVE_REGS; + + if (!domain) + /* + * Raise an exception to prevent the managed code from executing a pop + * later. + */ + mono_raise_exception (mono_get_exception_appdomain_unloaded ()); + + mono_thread_push_appdomain_ref (domain); +} + +void +ves_icall_System_AppDomain_InternalPopDomainRef (void) +{ + MONO_ARCH_SAVE_REGS; + + mono_thread_pop_appdomain_ref (); +} + MonoAppContext * ves_icall_System_AppDomain_InternalGetContext () { @@ -949,3 +1123,154 @@ ves_icall_System_AppDomain_InternalGetProcessGuid (MonoString* newguid) mono_domain_unlock (mono_root_domain); return newguid; } + +gboolean +mono_domain_is_unloading (MonoDomain *domain) +{ + if (domain->state == MONO_APPDOMAIN_UNLOADING || domain->state == MONO_APPDOMAIN_UNLOADED) + return TRUE; + else + return FALSE; +} + +static void +clear_cached_vtable (gpointer key, gpointer value, gpointer user_data) +{ + MonoClass *klass = (MonoClass*)key; + MonoDomain *domain = (MonoDomain*)user_data; + MonoVTable *vt; + + vt = klass->cached_vtable; + if (vt && vt->domain == domain) + klass->cached_vtable = NULL; +} + +typedef struct unload_data { + MonoDomain *domain; + char *failure_reason; +} unload_data; + +static guint32 +unload_thread_main (void *arg) +{ + unload_data *data = (unload_data*)arg; + MonoDomain *domain = data->domain; + + /* + * FIXME: Abort our parent thread last, so we can return a failure + * indication if aborting times out. + */ + if (!mono_threads_abort_appdomain_threads (domain, 10000)) { + data->failure_reason = g_strdup_printf ("Aborting of threads in domain %s timed out.", domain->friendly_name); + return 1; + } + + /* Finalize all finalizable objects in the doomed appdomain */ + if (!mono_domain_finalize (domain, 10000)) { + data->failure_reason = g_strdup_printf ("Finalization of domain %s timed out.", domain->friendly_name); + return 1; + } + + /* Clear references to our vtables in class->cached_vtable */ + mono_domain_lock (domain); + mono_g_hash_table_foreach (domain->class_vtable_hash, clear_cached_vtable, + domain); + mono_domain_unlock (domain); + + domain->state = MONO_APPDOMAIN_UNLOADED; + + //printf ("UNLOADED %s.\n", domain->friendly_name); + + mono_domain_free (domain, FALSE); + +#ifdef HAVE_BOEHM_GC + GC_gcollect (); +#endif + + return 0; +} + +/* + * mono_domain_unload: + * + * Unloads an appdomain. Follows the process outlined in: + * http://blogs.gotdotnet.com/cbrumme + * If doing things the 'right' way is too hard or complex, we do it the + * 'simple' way, which means do everything needed to avoid crashes and + * memory leaks, but not much else. + */ +static void +mono_domain_unload (MonoDomain *domain) +{ + HANDLE thread_handle; + guint32 tid; + MonoAppDomainState prev_state; + MonoMethod *method; + MonoObject *exc; + unload_data thread_data; + + //printf ("UNLOAD STARTING FOR %s.\n", domain->friendly_name); + + /* Atomically change our state to UNLOADING */ + prev_state = InterlockedCompareExchange (&domain->state, + MONO_APPDOMAIN_UNLOADING, + MONO_APPDOMAIN_CREATED); + if (prev_state != MONO_APPDOMAIN_CREATED) { + if (prev_state == MONO_APPDOMAIN_UNLOADING) + mono_raise_exception (mono_get_exception_cannot_unload_appdomain ("Appdomain is already being unloaded.")); + else + if (prev_state == MONO_APPDOMAIN_UNLOADED) + mono_raise_exception (mono_get_exception_cannot_unload_appdomain ("Appdomain is already unloaded.")); + else + g_assert_not_reached (); + } + + /* Notify OnDomainUnload listeners */ + method = look_for_method_by_name (domain->domain->mbr.obj.vtable->klass, "DoDomainUnload"); + g_assert (method); + + exc = NULL; + mono_runtime_invoke (method, domain->domain, NULL, &exc); + if (exc) { + /* Roll back the state change */ + domain->state = MONO_APPDOMAIN_CREATED; + mono_raise_exception ((MonoException*)exc); + } + + thread_data.domain = domain; + thread_data.failure_reason = NULL; + + /* + * First we create a separate thread for unloading, since + * we might have to abort some threads, including the current one. + */ + /* + * If we create a non-suspended thread, the runtime will hang. + * See: + * http://bugzilla.ximian.com/show_bug.cgi?id=27663 + */ +#if 0 + thread_handle = CreateThread (NULL, 0, unload_thread_main, &thread_data, 0, &tid); +#else + thread_handle = CreateThread (NULL, 0, unload_thread_main, &thread_data, CREATE_SUSPENDED, &tid); + ResumeThread (thread_handle); +#endif + + while (WaitForSingleObjectEx (thread_handle, INFINITE, FALSE) == WAIT_IO_COMPLETION) + ; /* wait for the thread */ + + + if (thread_data.failure_reason) { + MonoException *ex; + + ex = mono_get_exception_cannot_unload_appdomain (thread_data.failure_reason); + /* Roll back the state change */ + domain->state = MONO_APPDOMAIN_CREATED; + + g_warning (thread_data.failure_reason); + + g_free (thread_data.failure_reason); + + mono_raise_exception (ex); + } +}