X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmetadata%2Fappdomain.c;h=2858dd77e41137b02b0842e22ca24f54a68eda44;hb=4c0e461857603cb024c8d6f9a1652b6f3af81388;hp=6ca6b3b4743805ddcd8353225cb9263f9fdc12d1;hpb=3721f670cafb96c4313ad6383342f7b9fe0d9bc8;p=mono.git diff --git a/mono/metadata/appdomain.c b/mono/metadata/appdomain.c index 6ca6b3b4743..2858dd77e41 100644 --- a/mono/metadata/appdomain.c +++ b/mono/metadata/appdomain.c @@ -6,7 +6,8 @@ * Patrik Torstensson * Gonzalo Paniagua Javier (gonzalo@ximian.com) * - * (c) 2001-2003 Ximian, Inc. (http://www.ximian.com) + * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com) + * Copyright 2004-2009 Novell, Inc (http://www.novell.com) */ #undef ASSEMBLY_LOAD_DEBUG #include @@ -18,6 +19,9 @@ #include #include #include +#ifdef HAVE_SYS_TIME_H +#include +#endif #ifdef HAVE_UTIME_H #include #else @@ -41,6 +45,13 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include #include #include #include @@ -61,7 +72,7 @@ * Changes which are already detected at runtime, like the addition * of icalls, do not require an increment. */ -#define MONO_CORLIB_VERSION 66 +#define MONO_CORLIB_VERSION 82 typedef struct { @@ -72,11 +83,20 @@ typedef struct CRITICAL_SECTION mono_delegate_section; +#ifdef _EGLIB_MAJOR +/* Need to lock here because EGLIB has locking defined as no-ops, we can not depend on mono_strtod do the right locking */ +/* Ideally this will be fixed in eglib */ +CRITICAL_SECTION mono_strtod_mutex; +#endif + + static gunichar2 process_guid [36]; static gboolean process_guid_set = FALSE; static gboolean shutting_down = FALSE; +static gboolean no_exec = FALSE; + static MonoAssembly * mono_domain_assembly_preload (MonoAssemblyName *aname, gchar **assemblies_path, @@ -96,8 +116,11 @@ mono_domain_fire_assembly_load (MonoAssembly *assembly, gpointer user_data); static void add_assemblies_to_domain (MonoDomain *domain, MonoAssembly *ass, GHashTable *hash); -static void -mono_domain_unload (MonoDomain *domain); +static MonoAppDomain * +mono_domain_create_appdomain_internal (char *friendly_name, MonoAppDomainSetup *setup); + +static char * +get_shadow_assembly_location_base (MonoDomain *domain); static MonoLoadFunc load_function = NULL; @@ -114,6 +137,26 @@ mono_runtime_load (const char *filename, const char *runtime_version) return load_function (filename, runtime_version); } +/* + * mono_runtime_set_no_exec: + * + * Instructs the runtime to operate in static mode, i.e. avoid/do not allow managed + * code execution. This is useful for running the AOT compiler on platforms which + * allow full-aot execution only. + * This should be called before mono_runtime_init (). + */ +void +mono_runtime_set_no_exec (gboolean val) +{ + no_exec = val; +} + +gboolean +mono_runtime_get_no_exec (void) +{ + return no_exec; +} + /** * mono_runtime_init: * @domain: domain returned by mono_init () @@ -164,32 +207,45 @@ mono_runtime_init (MonoDomain *domain, MonoThreadStartCB start_cb, InitializeCriticalSection (&mono_delegate_section); +#ifdef _EGLIB_MAJOR + /* Needed until EGLIB is fixed #464316 */ + InitializeCriticalSection (&mono_strtod_mutex); +#endif + mono_thread_attach (domain); mono_context_init (domain); mono_context_set (domain->default_context); mono_type_initialization_init (); + if (!mono_runtime_get_no_exec ()) { + /* + * 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); - /* - * 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); - + /* + * 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 (); +#ifndef DISABLE_SOCKETS mono_network_init (); +#endif + + mono_console_init (); + mono_attach_init (); + + mono_locks_tracer_init (); /* mscorlib is loaded before we install the load hook */ mono_domain_fire_assembly_load (mono_defaults.corlib->assembly, NULL); @@ -252,13 +308,16 @@ mono_runtime_cleanup (MonoDomain *domain) { shutting_down = TRUE; + mono_attach_cleanup (); + /* This ends up calling any pending pending (for at most 2 seconds) */ mono_gc_cleanup (); mono_thread_cleanup (); +#ifndef DISABLE_SOCKETS mono_network_cleanup (); - +#endif mono_marshal_cleanup (); mono_type_initialization_cleanup (); @@ -312,6 +371,75 @@ mono_runtime_is_shutting_down (void) return shutting_down; } +/** + * mono_domain_create_appdomain: + * @friendly_name: The friendly name of the appdomain to create + * @configuration_file: The configuration file to initialize the appdomain with + * + * Returns a MonoDomain initialized with the appdomain + */ +MonoDomain * +mono_domain_create_appdomain (char *friendly_name, char *configuration_file) +{ + MonoAppDomain *ad; + MonoAppDomainSetup *setup; + MonoClass *class; + + class = mono_class_from_name (mono_defaults.corlib, "System", "AppDomainSetup"); + setup = (MonoAppDomainSetup *) mono_object_new (mono_domain_get (), class); + setup->configuration_file = configuration_file != NULL ? mono_string_new (mono_domain_get (), configuration_file) : NULL; + + ad = mono_domain_create_appdomain_internal (friendly_name, setup); + + return mono_domain_from_appdomain (ad); +} + +static MonoAppDomain * +mono_domain_create_appdomain_internal (char *friendly_name, MonoAppDomainSetup *setup) +{ + MonoClass *adclass; + MonoAppDomain *ad; + MonoDomain *data; + char *shadow_location; + + MONO_ARCH_SAVE_REGS; + + adclass = mono_class_from_name (mono_defaults.corlib, "System", "AppDomain"); + + /* FIXME: pin all those objects */ + data = mono_domain_create(); + + ad = (MonoAppDomain *) mono_object_new (data, adclass); + ad->data = data; + data->domain = ad; + data->setup = setup; + data->friendly_name = g_strdup (friendly_name); + + if (!setup->application_base) { + /* Inherit from the root domain since MS.NET does this */ + MonoDomain *root = mono_get_root_domain (); + if (root->setup->application_base) + MONO_OBJECT_SETREF (setup, application_base, mono_string_new_utf16 (data, mono_string_chars (root->setup->application_base), mono_string_length (root->setup->application_base))); + } + + mono_context_init (data); + + mono_set_private_bin_path_from_config (data); + + add_assemblies_to_domain (data, mono_defaults.corlib->assembly, NULL); + +#ifndef DISABLE_SHADOW_COPY + shadow_location = get_shadow_assembly_location_base (data); + mono_debugger_event_create_appdomain (data, shadow_location); + g_free (shadow_location); +#endif + + // FIXME: Initialize null_reference_ex and stack_overflow_ex + data->out_of_memory_ex = mono_exception_from_name_domain (data, mono_defaults.corlib, "System", "OutOfMemoryException"); + + return ad; +} + /** * mono_domain_has_type_resolve: * @domain: application domains being looked up @@ -596,12 +724,11 @@ mono_parser = { NULL }; -static void +void mono_set_private_bin_path_from_config (MonoDomain *domain) { gchar *config_file, *text; gsize len; - struct stat sbuf; GMarkupParseContext *context; RuntimeConfig runtime_config; @@ -609,10 +736,6 @@ mono_set_private_bin_path_from_config (MonoDomain *domain) return; config_file = mono_string_to_utf8 (domain->setup->configuration_file); - if (stat (config_file, &sbuf) != 0) { - g_free (config_file); - return; - } if (!g_file_get_contents (config_file, &text, &len, NULL)) { g_free (config_file); @@ -634,38 +757,10 @@ mono_set_private_bin_path_from_config (MonoDomain *domain) MonoAppDomain * ves_icall_System_AppDomain_createDomain (MonoString *friendly_name, MonoAppDomainSetup *setup) { - MonoClass *adclass; - MonoAppDomain *ad; - MonoDomain *data; - - MONO_ARCH_SAVE_REGS; - - adclass = mono_class_from_name (mono_defaults.corlib, "System", "AppDomain"); - - /* FIXME: pin all those objects */ - data = mono_domain_create(); - - ad = (MonoAppDomain *) mono_object_new (data, adclass); - ad->data = data; - data->domain = ad; - data->setup = setup; - data->friendly_name = mono_string_to_utf8 (friendly_name); - // FIXME: The ctor runs in the current domain - // FIXME: Initialize null_reference_ex and stack_overflow_ex - data->out_of_memory_ex = mono_exception_from_name_domain (data, mono_defaults.corlib, "System", "OutOfMemoryException"); - - if (!setup->application_base) { - /* Inherit from the root domain since MS.NET does this */ - MonoDomain *root = mono_get_root_domain (); - if (root->setup->application_base) - MONO_OBJECT_SETREF (setup, application_base, mono_string_new_utf16 (data, mono_string_chars (root->setup->application_base), mono_string_length (root->setup->application_base))); - } - - mono_set_private_bin_path_from_config (data); + char *fname = mono_string_to_utf8 (friendly_name); + MonoAppDomain *ad = mono_domain_create_appdomain_internal (fname, setup); - mono_context_init (data); - - add_assemblies_to_domain (data, mono_defaults.corlib->assembly, NULL); + g_free (fname); return ad; } @@ -678,37 +773,40 @@ ves_icall_System_AppDomain_GetAssemblies (MonoAppDomain *ad, MonoBoolean refonly static MonoClass *System_Reflection_Assembly; MonoArray *res; GSList *tmp; - int i, count; - + int i; + GPtrArray *assemblies; + MONO_ARCH_SAVE_REGS; if (!System_Reflection_Assembly) System_Reflection_Assembly = mono_class_from_name ( mono_defaults.corlib, "System.Reflection", "Assembly"); - count = 0; + /* + * Make a copy of the list of assemblies because we can't hold the assemblies + * lock while creating objects etc. + */ + assemblies = g_ptr_array_new (); /* Need to skip internal assembly builders created by remoting */ mono_domain_assemblies_lock (domain); for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) { ass = tmp->data; - if (refonly && !ass->ref_only) - continue; - if (!ass->corlib_internal) - count++; - } - res = mono_array_new (domain, System_Reflection_Assembly, count); - i = 0; - for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) { - ass = tmp->data; - if (refonly && !ass->ref_only) + if (refonly != ass->ref_only) continue; if (ass->corlib_internal) continue; - mono_array_setref (res, i, mono_assembly_get_object (domain, ass)); - ++i; + g_ptr_array_add (assemblies, ass); } mono_domain_assemblies_unlock (domain); + res = mono_array_new (domain, System_Reflection_Assembly, assemblies->len); + for (i = 0; i < assemblies->len; ++i) { + ass = g_ptr_array_index (assemblies, i); + mono_array_setref (res, i, mono_assembly_get_object (domain, ass)); + } + + g_ptr_array_free (assemblies, TRUE); + return res; } @@ -720,6 +818,9 @@ mono_try_assembly_resolve (MonoDomain *domain, MonoString *fname, gboolean refon MonoBoolean isrefonly; gpointer params [2]; + if (mono_runtime_get_no_exec ()) + return NULL; + g_assert (domain != NULL && fname != NULL); klass = domain->domain->mbr.obj.vtable->klass; @@ -846,6 +947,9 @@ mono_domain_fire_assembly_load (MonoAssembly *assembly, gpointer user_data) mono_runtime_invoke (assembly_load_method, domain->domain, params, NULL); } +/* + * LOCKING: Acquires the domain assemblies lock. + */ static void set_domain_search_path (MonoDomain *domain) { @@ -858,14 +962,26 @@ set_domain_search_path (MonoDomain *domain) GError *error = NULL; gint appbaselen = -1; - if ((domain->search_path != NULL) && !domain->setup->path_changed) - return; - if (!domain->setup) + /* + * We use the low-level domain assemblies lock, since this is called from + * assembly loads hooks, which means this thread might hold the loader lock. + */ + mono_domain_assemblies_lock (domain); + + if (!domain->setup) { + mono_domain_assemblies_unlock (domain); return; + } + if ((domain->search_path != NULL) && !domain->setup->path_changed) { + mono_domain_assemblies_unlock (domain); + return; + } setup = domain->setup; - if (!setup->application_base) + if (!setup->application_base) { + mono_domain_assemblies_unlock (domain); return; /* Must set application base to get private path working */ + } npaths++; @@ -918,6 +1034,7 @@ set_domain_search_path (MonoDomain *domain) * domain->search_path = g_malloc (sizeof (char *)); * domain->search_path [0] = NULL; */ + mono_domain_assemblies_unlock (domain); return; } @@ -991,8 +1108,23 @@ set_domain_search_path (MonoDomain *domain) domain->setup->path_changed = FALSE; g_strfreev (pvt_split); + + mono_domain_assemblies_unlock (domain); } +#ifdef DISABLE_SHADOW_COPY +gboolean +mono_is_shadow_copy_enabled (MonoDomain *domain, const gchar *dir_name) +{ + return FALSE; +} + +char * +mono_make_shadow_copy (const char *filename) +{ + return (char *) filename; +} +#else static gboolean shadow_copy_sibling (gchar *src, gint srclen, const char *extension, gchar *target, gint targetlen, gint tail_len) { @@ -1035,6 +1167,41 @@ get_cstring_hash (const char *str) return h; } +/* + * Returned memory is malloc'd. Called must free it + */ +static char * +get_shadow_assembly_location_base (MonoDomain *domain) +{ + MonoAppDomainSetup *setup; + char *cache_path, *appname; + char *userdir; + char *location; + + setup = domain->setup; + if (setup->cache_path != NULL && setup->application_name != NULL) { + cache_path = mono_string_to_utf8 (setup->cache_path); +#ifndef PLATFORM_WIN32 + { + gint i; + for (i = strlen (cache_path) - 1; i >= 0; i--) + if (cache_path [i] == '\\') + cache_path [i] = '/'; + } +#endif + + appname = mono_string_to_utf8 (setup->application_name); + location = g_build_filename (cache_path, appname, "assembly", "shadow", NULL); + g_free (appname); + g_free (cache_path); + } else { + userdir = g_strdup_printf ("%s-mono-cachepath", g_get_user_name ()); + location = g_build_filename (g_get_tmp_dir (), userdir, "assembly", "shadow", NULL); + g_free (userdir); + } + return location; +} + static char * get_shadow_assembly_location (const char *filename) { @@ -1043,16 +1210,16 @@ get_shadow_assembly_location (const char *filename) char path_hash [30]; char *bname = g_path_get_basename (filename); char *dirname = g_path_get_dirname (filename); - char *location, *dyn_base; + char *location, *tmploc; MonoDomain *domain = mono_domain_get (); hash = get_cstring_hash (bname); hash2 = get_cstring_hash (dirname); g_snprintf (name_hash, sizeof (name_hash), "%08x", hash); g_snprintf (path_hash, sizeof (path_hash), "%08x_%08x_%08x", hash ^ hash2, hash2, domain->shadow_serial); - dyn_base = mono_string_to_utf8 (domain->setup->dynamic_base); - location = g_build_filename (dyn_base, "assembly", "shadow", name_hash, path_hash, bname, NULL); - g_free (dyn_base); + tmploc = get_shadow_assembly_location_base (domain); + location = g_build_filename (tmploc, name_hash, path_hash, bname, NULL); + g_free (tmploc); g_free (bname); g_free (dirname); return location; @@ -1114,10 +1281,18 @@ ensure_directory_exists (const char *filename) char *p; gchar *dir = g_path_get_dirname (filename); int retval; + struct stat sbuf; - if (!dir || !dir [0]) + if (!dir || !dir [0]) { + g_free (dir); return FALSE; - + } + + if (stat (dir, &sbuf) == 0 && S_ISDIR (sbuf.st_mode)) { + g_free (dir); + return TRUE; + } + p = dir; while (*p == '/') p++; @@ -1157,6 +1332,98 @@ private_file_needs_copying (const char *src, struct stat *sbuf_src, char *dest) return TRUE; } +static gboolean +shadow_copy_create_ini (const char *shadow, const char *filename) +{ + char *dir_name; + char *ini_file; + guint16 *u16_ini; + gboolean result; + guint32 n; + HANDLE *handle; + gchar *full_path; + + dir_name = g_path_get_dirname (shadow); + ini_file = g_build_filename (dir_name, "__AssemblyInfo__.ini", NULL); + g_free (dir_name); + if (g_file_test (ini_file, G_FILE_TEST_IS_REGULAR)) { + g_free (ini_file); + return TRUE; + } + + u16_ini = g_utf8_to_utf16 (ini_file, strlen (ini_file), NULL, NULL, NULL); + g_free (ini_file); + if (!u16_ini) { + return FALSE; + } + handle = CreateFile (u16_ini, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, + NULL, CREATE_NEW, FileAttributes_Normal, NULL); + g_free (u16_ini); + if (handle == INVALID_HANDLE_VALUE) { + return FALSE; + } + + full_path = mono_path_resolve_symlinks (filename); + result = WriteFile (handle, full_path, strlen (full_path), &n, NULL); + g_free (full_path); + CloseHandle (handle); + return result; +} + +gboolean +mono_is_shadow_copy_enabled (MonoDomain *domain, const gchar *dir_name) +{ + const char *version; + MonoAppDomainSetup *setup; + gchar *all_dirs; + gchar **dir_ptr; + gchar **directories; + gchar *shadow_status_string; + gchar *base_dir; + gboolean shadow_enabled; + gboolean found = FALSE; + + if (domain == NULL) + return FALSE; + + setup = domain->setup; + if (setup == NULL || setup->shadow_copy_files == NULL) + return FALSE; + + version = mono_get_runtime_info ()->framework_version; + shadow_status_string = mono_string_to_utf8 (setup->shadow_copy_files); + /* For 1.x, not NULL is enough. In 2.0 it has to be "true" */ + shadow_enabled = (*version <= '1' || !g_ascii_strncasecmp (shadow_status_string, "true", 4)); + g_free (shadow_status_string); + if (!shadow_enabled) + return FALSE; + + if (setup->shadow_copy_directories == NULL) + return TRUE; + + /* Is dir_name a shadow_copy destination already? */ + base_dir = get_shadow_assembly_location_base (domain); + if (strstr (dir_name, base_dir)) { + g_free (base_dir); + return TRUE; + } + g_free (base_dir); + + all_dirs = mono_string_to_utf8 (setup->shadow_copy_directories); + directories = g_strsplit (all_dirs, G_SEARCHPATH_SEPARATOR_S, 1000); + dir_ptr = directories; + while (*dir_ptr) { + if (**dir_ptr != '\0' && !strcmp (*dir_ptr, dir_name)) { + found = TRUE; + break; + } + dir_ptr++; + } + g_strfreev (directories); + g_free (all_dirs); + return found; +} + char * mono_make_shadow_copy (const char *filename) { @@ -1165,39 +1432,32 @@ mono_make_shadow_copy (const char *filename) guint16 *orig, *dest; char *shadow; gboolean copy_result; - gboolean is_private = FALSE; - gboolean do_copy = FALSE; MonoException *exc; - gchar **path; struct stat src_sbuf; struct utimbuf utbuf; char *dir_name = g_path_get_dirname (filename); MonoDomain *domain = mono_domain_get (); - set_domain_search_path (domain); + char *shadow_dir; - if (!domain->search_path) - return (char*) filename; - - for (path = domain->search_path; *path; path++) { - if (**path == '\0') { - is_private = TRUE; - continue; - } - - if (!is_private) - continue; + set_domain_search_path (domain); - if (strcmp (dir_name, *path) == 0) { - do_copy = TRUE; - break; - } + if (!mono_is_shadow_copy_enabled (domain, dir_name)) { + g_free (dir_name); + return (char *) filename; + } + /* Is dir_name a shadow_copy destination already? */ + shadow_dir = get_shadow_assembly_location_base (domain); + if (strstr (dir_name, shadow_dir)) { + g_free (shadow_dir); + g_free (dir_name); + return (char *) filename; } + g_free (shadow_dir); + g_free (dir_name); - if (!do_copy) - return (char*) filename; - shadow = get_shadow_assembly_location (filename); if (ensure_directory_exists (shadow) == FALSE) { + g_free (shadow); exc = mono_get_exception_execution_engine ("Failed to create shadow copy (ensure directory exists)."); mono_raise_exception (exc); } @@ -1237,13 +1497,20 @@ mono_make_shadow_copy (const char *filename) mono_raise_exception (exc); } + /* Create a .ini file containing the original assembly location */ + if (!shadow_copy_create_ini (shadow, filename)) { + g_free (shadow); + exc = mono_get_exception_execution_engine ("Failed to create shadow copy .ini file."); + mono_raise_exception (exc); + } + utbuf.actime = src_sbuf.st_atime; utbuf.modtime = src_sbuf.st_mtime; utime (shadow, &utbuf); return shadow; } - +#endif /* DISABLE_SHADOW_COPY */ MonoDomain * mono_domain_from_appdomain (MonoAppDomain *appdomain) @@ -1336,6 +1603,8 @@ real_load (gchar **search_path, const gchar *culture, const gchar *name, gboolea /* * Try loading the assembly from ApplicationBase and PrivateBinPath * and then from assemblies_path if any. + * LOCKING: This is called from the assembly loading code, which means the caller + * might hold the loader lock. Thus, this function must not acquire the domain lock. */ static MonoAssembly * mono_domain_assembly_preload (MonoAssemblyName *aname, @@ -1476,11 +1745,8 @@ ves_icall_System_AppDomain_LoadAssembly (MonoAppDomain *ad, MonoString *assRef, g_free (name); if (!parsed) { - MonoException *exc; - /* This is a parse error... */ - exc = mono_get_exception_file_not_found2 (NULL, assRef); - mono_raise_exception (exc); + return NULL; } ass = mono_assembly_load_full_nosearch (&aname, NULL, &status, refOnly); @@ -1493,9 +1759,7 @@ ves_icall_System_AppDomain_LoadAssembly (MonoAppDomain *ad, MonoString *assRef, else refass = NULL; if (!refass) { - /* FIXME: it doesn't make much sense since we really don't have a filename ... */ - MonoException *exc = mono_get_exception_file_not_found2 (NULL, assRef); - mono_raise_exception (exc); + return NULL; } } @@ -1692,12 +1956,15 @@ static void clear_cached_vtable (gpointer key, gpointer value, gpointer user_data) { MonoClass *klass = (MonoClass*)key; + MonoVTable *vtable = value; MonoDomain *domain = (MonoDomain*)user_data; MonoClassRuntimeInfo *runtime_info; runtime_info = klass->runtime_info; if (runtime_info && runtime_info->max_domain >= domain->domain_id) runtime_info->domain_vtables [domain->domain_id] = NULL; + if (vtable->data && klass->has_static_refs) + mono_gc_free_fixed (vtable->data); } typedef struct unload_data { @@ -1705,23 +1972,99 @@ typedef struct unload_data { char *failure_reason; } unload_data; +#ifdef HAVE_SGEN_GC +static void +deregister_reflection_info_roots_nspace_table (gpointer key, gpointer value, gpointer image) +{ + guint32 index = GPOINTER_TO_UINT (value); + MonoClass *class = mono_class_get (image, MONO_TOKEN_TYPE_DEF | index); + + g_assert (class); + + if (class->reflection_info) + mono_gc_deregister_root ((char*) &class->reflection_info); +} + +static void +deregister_reflection_info_roots_name_space (gpointer key, gpointer value, gpointer user_data) +{ + g_hash_table_foreach (value, deregister_reflection_info_roots_nspace_table, user_data); +} + +static void +deregister_reflection_info_roots_from_list (MonoImage *image) +{ + GSList *list = image->reflection_info_unregister_classes; + + while (list) { + MonoClass *class = list->data; + + g_assert (class->reflection_info); + mono_gc_deregister_root ((char*) &class->reflection_info); + + list = list->next; + } + + g_slist_free (image->reflection_info_unregister_classes); + image->reflection_info_unregister_classes = NULL; +} + +static void +deregister_reflection_info_roots (MonoDomain *domain) +{ + GSList *list; + + mono_loader_lock (); + mono_domain_assemblies_lock (domain); + for (list = domain->domain_assemblies; list; list = list->next) { + MonoAssembly *assembly = list->data; + MonoImage *image = assembly->image; + int i; + /*No need to take the image lock here since dynamic images are appdomain bound and at this point the mutator is gone.*/ + if (image->dynamic && image->name_cache) + g_hash_table_foreach (image->name_cache, deregister_reflection_info_roots_name_space, image); + deregister_reflection_info_roots_from_list (image); + for (i = 0; i < image->module_count; ++i) { + MonoImage *module = image->modules [i]; + if (module) { + if (module->dynamic && module->name_cache) { + g_hash_table_foreach (module->name_cache, + deregister_reflection_info_roots_name_space, module); + } + deregister_reflection_info_roots_from_list (module); + } + } + } + mono_domain_assemblies_unlock (domain); + mono_loader_unlock (); +} +#endif + static guint32 WINAPI unload_thread_main (void *arg) { unload_data *data = (unload_data*)arg; MonoDomain *domain = data->domain; + /* Have to attach to the runtime so shutdown can wait for this thread */ + mono_thread_attach (mono_get_root_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)) { + if (!mono_threads_abort_appdomain_threads (domain, -1)) { data->failure_reason = g_strdup_printf ("Aborting of threads in domain %s timed out.", domain->friendly_name); return 1; } + if (!mono_thread_pool_remove_domain_jobs (domain, -1)) { + data->failure_reason = g_strdup_printf ("Cleanup of threadpool jobs of domain %s timed out.", domain->friendly_name); + return 1; + } + /* Finalize all finalizable objects in the doomed appdomain */ - if (!mono_domain_finalize (domain, 10000)) { + if (!mono_domain_finalize (domain, -1)) { data->failure_reason = g_strdup_printf ("Finalization of domain %s timed out.", domain->friendly_name); return 1; } @@ -1730,11 +2073,15 @@ unload_thread_main (void *arg) * We also hold the loader lock because we're going to change * class->runtime_info. */ - mono_domain_lock (domain); + mono_loader_lock (); + mono_domain_lock (domain); g_hash_table_foreach (domain->class_vtable_hash, clear_cached_vtable, domain); - mono_loader_unlock (); +#ifdef HAVE_SGEN_GC + deregister_reflection_info_roots (domain); +#endif mono_domain_unlock (domain); + mono_loader_unlock (); mono_threads_clear_cached_culture (domain); @@ -1763,7 +2110,7 @@ unload_thread_main (void *arg) * 'simple' way, which means do everything needed to avoid crashes and * memory leaks, but not much else. */ -static void +void mono_domain_unload (MonoDomain *domain) { HANDLE thread_handle; @@ -1779,18 +2126,25 @@ mono_domain_unload (MonoDomain *domain) /* Atomically change our state to UNLOADING */ prev_state = InterlockedCompareExchange ((gint32*)&domain->state, - MONO_APPDOMAIN_UNLOADING, + MONO_APPDOMAIN_UNLOADING_START, MONO_APPDOMAIN_CREATED); if (prev_state != MONO_APPDOMAIN_CREATED) { - if (prev_state == MONO_APPDOMAIN_UNLOADING) + switch (prev_state) { + case MONO_APPDOMAIN_UNLOADING_START: + case 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 + break; + case MONO_APPDOMAIN_UNLOADED: + mono_raise_exception (mono_get_exception_cannot_unload_appdomain ("Appdomain is already unloaded.")); + break; + default: + g_warning ("Incalid appdomain state %d", prev_state); g_assert_not_reached (); + } } + mono_debugger_event_unload_appdomain (domain); + mono_domain_set (domain, FALSE); /* Notify OnDomainUnload listeners */ method = mono_class_get_method_from_name (domain->domain->mbr.obj.vtable->klass, "DoDomainUnload", -1); @@ -1804,10 +2158,13 @@ mono_domain_unload (MonoDomain *domain) mono_domain_set (caller_domain, FALSE); mono_raise_exception ((MonoException*)exc); } + mono_domain_set (caller_domain, FALSE); thread_data.domain = domain; thread_data.failure_reason = NULL; + /*The managed callback finished successfully, now we start tearing down the appdomain*/ + domain->state = MONO_APPDOMAIN_UNLOADING; /* * First we create a separate thread for unloading, since * we might have to abort some threads, including the current one. @@ -1818,9 +2175,9 @@ mono_domain_unload (MonoDomain *domain) * http://bugzilla.ximian.com/show_bug.cgi?id=27663 */ #if 0 - thread_handle = CreateThread (NULL, 0, unload_thread_main, &thread_data, 0, &tid); + thread_handle = mono_create_thread (NULL, 0, unload_thread_main, &thread_data, 0, &tid); #else - thread_handle = CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE)unload_thread_main, &thread_data, CREATE_SUSPENDED, &tid); + thread_handle = mono_create_thread (NULL, 0, (LPTHREAD_START_ROUTINE)unload_thread_main, &thread_data, CREATE_SUSPENDED, &tid); if (thread_handle == NULL) { return; } @@ -1837,15 +2194,13 @@ mono_domain_unload (MonoDomain *domain) } CloseHandle (thread_handle); - mono_domain_set (caller_domain, FALSE); - if (thread_data.failure_reason) { MonoException *ex; /* Roll back the state change */ domain->state = MONO_APPDOMAIN_CREATED; - g_warning (thread_data.failure_reason); + g_warning ("%s", thread_data.failure_reason); ex = mono_get_exception_cannot_unload_appdomain (thread_data.failure_reason);