[coop] Add missing coop checks for WFSO and WFMO.
[mono-project.git] / mono / metadata / appdomain.c
blobb010c10b5d7dcaf289b98933f0096c65915917d1
1 /*
2 * appdomain.c: AppDomain functions
4 * Authors:
5 * Dietmar Maurer (dietmar@ximian.com)
6 * Patrik Torstensson
7 * Gonzalo Paniagua Javier (gonzalo@ximian.com)
9 * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
10 * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
11 * Copyright 2012 Xamarin Inc
13 #undef ASSEMBLY_LOAD_DEBUG
14 #include <config.h>
15 #include <glib.h>
16 #include <string.h>
17 #include <errno.h>
18 #include <time.h>
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #ifdef HAVE_SYS_TIME_H
22 #include <sys/time.h>
23 #endif
24 #ifdef HAVE_UNISTD_H
25 #include <unistd.h>
26 #endif
27 #ifdef HAVE_UTIME_H
28 #include <utime.h>
29 #else
30 #ifdef HAVE_SYS_UTIME_H
31 #include <sys/utime.h>
32 #endif
33 #endif
35 #include <mono/metadata/gc-internal.h>
36 #include <mono/metadata/object.h>
37 #include <mono/metadata/domain-internals.h>
38 #include "mono/metadata/metadata-internals.h"
39 #include <mono/metadata/assembly.h>
40 #include <mono/metadata/exception.h>
41 #include <mono/metadata/threads.h>
42 #include <mono/metadata/socket-io.h>
43 #include <mono/metadata/tabledefs.h>
44 #include <mono/metadata/gc-internal.h>
45 #include <mono/metadata/mono-gc.h>
46 #include <mono/metadata/marshal.h>
47 #include <mono/metadata/monitor.h>
48 #include <mono/metadata/threadpool.h>
49 #include <mono/metadata/mono-debug.h>
50 #include <mono/metadata/mono-debug-debugger.h>
51 #include <mono/metadata/attach.h>
52 #include <mono/metadata/file-io.h>
53 #include <mono/metadata/lock-tracer.h>
54 #include <mono/metadata/console-io.h>
55 #include <mono/metadata/threads-types.h>
56 #include <mono/metadata/tokentype.h>
57 #include <mono/utils/mono-uri.h>
58 #include <mono/utils/mono-logger-internal.h>
59 #include <mono/utils/mono-path.h>
60 #include <mono/utils/mono-stdlib.h>
61 #include <mono/utils/mono-io-portability.h>
62 #include <mono/utils/mono-error-internals.h>
63 #include <mono/utils/atomic.h>
64 #include <mono/utils/mono-memory-model.h>
65 #include <mono/utils/mono-threads.h>
66 #ifdef HOST_WIN32
67 #include <direct.h>
68 #endif
71 * This is the version number of the corlib-runtime interface. When
72 * making changes to this interface (by changing the layout
73 * of classes the runtime knows about, changing icall signature or
74 * semantics etc), increment this variable. Also increment the
75 * pair of this variable in mscorlib in:
76 * mcs/class/corlib/System/Environment.cs
78 * Changes which are already detected at runtime, like the addition
79 * of icalls, do not require an increment.
81 #define MONO_CORLIB_VERSION 129
83 typedef struct
85 int runtime_count;
86 int assemblybinding_count;
87 MonoDomain *domain;
88 gchar *filename;
89 } RuntimeConfig;
91 mono_mutex_t mono_delegate_section;
93 mono_mutex_t mono_strtod_mutex;
95 static gunichar2 process_guid [36];
96 static gboolean process_guid_set = FALSE;
98 static gboolean no_exec = FALSE;
100 static MonoAssembly *
101 mono_domain_assembly_preload (MonoAssemblyName *aname,
102 gchar **assemblies_path,
103 gpointer user_data);
105 static MonoAssembly *
106 mono_domain_assembly_search (MonoAssemblyName *aname,
107 gpointer user_data);
109 static void
110 mono_domain_fire_assembly_load (MonoAssembly *assembly, gpointer user_data);
112 static void
113 add_assemblies_to_domain (MonoDomain *domain, MonoAssembly *ass, GHashTable *hash);
115 static MonoAppDomain *
116 mono_domain_create_appdomain_internal (char *friendly_name, MonoAppDomainSetup *setup);
118 static char *
119 get_shadow_assembly_location_base (MonoDomain *domain, MonoError *error);
121 static MonoLoadFunc load_function = NULL;
123 void
124 mono_install_runtime_load (MonoLoadFunc func)
126 load_function = func;
129 MonoDomain*
130 mono_runtime_load (const char *filename, const char *runtime_version)
132 g_assert (load_function);
133 return load_function (filename, runtime_version);
137 * mono_runtime_set_no_exec:
139 * Instructs the runtime to operate in static mode, i.e. avoid/do not
140 * allow managed code execution. This is useful for running the AOT
141 * compiler on platforms which allow full-aot execution only. This
142 * should be called before mono_runtime_init ().
144 void
145 mono_runtime_set_no_exec (gboolean val)
147 no_exec = val;
151 * mono_runtime_get_no_exec:
153 * If true, then the runtime will not allow managed code execution.
155 gboolean
156 mono_runtime_get_no_exec (void)
158 return no_exec;
161 static void
162 create_domain_objects (MonoDomain *domain)
164 MonoDomain *old_domain = mono_domain_get ();
165 MonoString *arg;
166 MonoVTable *string_vt;
167 MonoClassField *string_empty_fld;
169 if (domain != old_domain) {
170 mono_thread_push_appdomain_ref (domain);
171 mono_domain_set_internal_with_options (domain, FALSE);
175 * Initialize String.Empty. This enables the removal of
176 * the static cctor of the String class.
178 string_vt = mono_class_vtable (domain, mono_defaults.string_class);
179 string_empty_fld = mono_class_get_field_from_name (mono_defaults.string_class, "Empty");
180 g_assert (string_empty_fld);
181 mono_field_static_set_value (string_vt, string_empty_fld, mono_string_intern (mono_string_new (domain, "")));
184 * Create an instance early since we can't do it when there is no memory.
186 arg = mono_string_new (domain, "Out of memory");
187 domain->out_of_memory_ex = mono_exception_from_name_two_strings (mono_defaults.corlib, "System", "OutOfMemoryException", arg, NULL);
190 * These two are needed because the signal handlers might be executing on
191 * an alternate stack, and Boehm GC can't handle that.
193 arg = mono_string_new (domain, "A null value was found where an object instance was required");
194 domain->null_reference_ex = mono_exception_from_name_two_strings (mono_defaults.corlib, "System", "NullReferenceException", arg, NULL);
195 arg = mono_string_new (domain, "The requested operation caused a stack overflow.");
196 domain->stack_overflow_ex = mono_exception_from_name_two_strings (mono_defaults.corlib, "System", "StackOverflowException", arg, NULL);
198 /*The ephemeron tombstone i*/
199 domain->ephemeron_tombstone = mono_object_new (domain, mono_defaults.object_class);
201 if (domain != old_domain) {
202 mono_thread_pop_appdomain_ref ();
203 mono_domain_set_internal_with_options (old_domain, FALSE);
207 * This class is used during exception handling, so initialize it here, to prevent
208 * stack overflows while handling stack overflows.
210 mono_class_init (mono_array_class_get (mono_defaults.int_class, 1));
214 * mono_runtime_init:
215 * @domain: domain returned by mono_init ()
217 * Initialize the core AppDomain: this function will run also some
218 * IL initialization code, so it needs the execution engine to be fully
219 * operational.
221 * AppDomain.SetupInformation is set up in mono_runtime_exec_main, where
222 * we know the entry_assembly.
225 void
226 mono_runtime_init (MonoDomain *domain, MonoThreadStartCB start_cb,
227 MonoThreadAttachCB attach_cb)
229 MonoAppDomainSetup *setup;
230 MonoAppDomain *ad;
231 MonoClass *class;
233 mono_portability_helpers_init ();
235 mono_gc_base_init ();
236 mono_monitor_init ();
237 mono_thread_pool_init ();
238 mono_marshal_init ();
240 mono_install_assembly_preload_hook (mono_domain_assembly_preload, GUINT_TO_POINTER (FALSE));
241 mono_install_assembly_refonly_preload_hook (mono_domain_assembly_preload, GUINT_TO_POINTER (TRUE));
242 mono_install_assembly_search_hook (mono_domain_assembly_search, GUINT_TO_POINTER (FALSE));
243 mono_install_assembly_refonly_search_hook (mono_domain_assembly_search, GUINT_TO_POINTER (TRUE));
244 mono_install_assembly_postload_search_hook ((void*)mono_domain_assembly_postload_search, GUINT_TO_POINTER (FALSE));
245 mono_install_assembly_postload_refonly_search_hook ((void*)mono_domain_assembly_postload_search, GUINT_TO_POINTER (TRUE));
246 mono_install_assembly_load_hook (mono_domain_fire_assembly_load, NULL);
247 mono_install_lookup_dynamic_token (mono_reflection_lookup_dynamic_token);
249 mono_thread_init (start_cb, attach_cb);
251 class = mono_class_from_name (mono_defaults.corlib, "System", "AppDomainSetup");
252 setup = (MonoAppDomainSetup *) mono_object_new_pinned (domain, class);
254 class = mono_class_from_name (mono_defaults.corlib, "System", "AppDomain");
255 ad = (MonoAppDomain *) mono_object_new_pinned (domain, class);
256 ad->data = domain;
257 domain->domain = ad;
258 domain->setup = setup;
260 mono_mutex_init_recursive (&mono_delegate_section);
262 mono_mutex_init_recursive (&mono_strtod_mutex);
264 mono_thread_attach (domain);
265 mono_context_init (domain);
266 mono_context_set (domain->default_context);
268 mono_type_initialization_init ();
270 if (!mono_runtime_get_no_exec ())
271 create_domain_objects (domain);
273 /* GC init has to happen after thread init */
274 mono_gc_init ();
276 #ifndef DISABLE_SOCKETS
277 mono_network_init ();
278 #endif
280 mono_console_init ();
281 mono_attach_init ();
283 mono_locks_tracer_init ();
285 /* mscorlib is loaded before we install the load hook */
286 mono_domain_fire_assembly_load (mono_defaults.corlib->assembly, NULL);
288 return;
291 static int
292 mono_get_corlib_version (void)
294 MonoClass *klass;
295 MonoClassField *field;
296 MonoObject *value;
298 klass = mono_class_from_name (mono_defaults.corlib, "System", "Environment");
299 mono_class_init (klass);
300 field = mono_class_get_field_from_name (klass, "mono_corlib_version");
301 if (!field)
302 return -1;
303 if (! (field->type->attrs & FIELD_ATTRIBUTE_STATIC))
304 return -1;
305 value = mono_field_get_value_object (mono_domain_get (), field, NULL);
306 return *(gint32*)((gchar*)value + sizeof (MonoObject));
310 * mono_check_corlib_version
312 * Checks that the corlib that is loaded matches the version of this runtime.
314 * Returns: NULL if the runtime will work with the corlib, or a g_malloc
315 * allocated string with the error otherwise.
317 const char*
318 mono_check_corlib_version (void)
320 int version = mono_get_corlib_version ();
321 if (version != MONO_CORLIB_VERSION)
322 return g_strdup_printf ("expected corlib version %d, found %d.", MONO_CORLIB_VERSION, version);
323 else
324 return NULL;
328 * mono_context_init:
329 * @domain: The domain where the System.Runtime.Remoting.Context.Context is initialized
331 * Initializes the @domain's default System.Runtime.Remoting's Context.
333 void
334 mono_context_init (MonoDomain *domain)
336 MonoClass *class;
337 MonoAppContext *context;
339 class = mono_class_from_name (mono_defaults.corlib, "System.Runtime.Remoting.Contexts", "Context");
340 context = (MonoAppContext *) mono_object_new_pinned (domain, class);
341 context->domain_id = domain->domain_id;
342 context->context_id = 0;
343 domain->default_context = context;
347 * mono_runtime_cleanup:
348 * @domain: unused.
350 * Internal routine.
352 * This must not be called while there are still running threads executing
353 * managed code.
355 void
356 mono_runtime_cleanup (MonoDomain *domain)
358 mono_attach_cleanup ();
360 /* This ends up calling any pending pending (for at most 2 seconds) */
361 mono_gc_cleanup ();
363 mono_thread_cleanup ();
365 #ifndef DISABLE_SOCKETS
366 mono_network_cleanup ();
367 #endif
368 mono_marshal_cleanup ();
370 mono_type_initialization_cleanup ();
372 mono_monitor_cleanup ();
375 static MonoDomainFunc quit_function = NULL;
377 void
378 mono_install_runtime_cleanup (MonoDomainFunc func)
380 quit_function = func;
383 void
384 mono_runtime_quit ()
386 if (quit_function != NULL)
387 quit_function (mono_get_root_domain (), NULL);
391 * mono_domain_create_appdomain:
392 * @friendly_name: The friendly name of the appdomain to create
393 * @configuration_file: The configuration file to initialize the appdomain with
395 * Returns a MonoDomain initialized with the appdomain
397 MonoDomain *
398 mono_domain_create_appdomain (char *friendly_name, char *configuration_file)
400 MonoAppDomain *ad;
401 MonoAppDomainSetup *setup;
402 MonoClass *class;
404 class = mono_class_from_name (mono_defaults.corlib, "System", "AppDomainSetup");
405 setup = (MonoAppDomainSetup *) mono_object_new (mono_domain_get (), class);
406 setup->configuration_file = configuration_file != NULL ? mono_string_new (mono_domain_get (), configuration_file) : NULL;
408 ad = mono_domain_create_appdomain_internal (friendly_name, setup);
410 return mono_domain_from_appdomain (ad);
414 * mono_domain_set_config:
415 * @domain: MonoDomain initialized with the appdomain we want to change
416 * @base_dir: new base directory for the appdomain
417 * @config_file_name: path to the new configuration for the app domain
419 * Used to set the system configuration for an appdomain
421 * Without using this, embedded builds will get 'System.Configuration.ConfigurationErrorsException:
422 * Error Initializing the configuration system. ---> System.ArgumentException:
423 * The 'ExeConfigFilename' argument cannot be null.' for some managed calls.
425 void
426 mono_domain_set_config (MonoDomain *domain, const char *base_dir, const char *config_file_name)
428 MONO_OBJECT_SETREF (domain->setup, application_base, mono_string_new (domain, base_dir));
429 MONO_OBJECT_SETREF (domain->setup, configuration_file, mono_string_new (domain, config_file_name));
432 static MonoAppDomainSetup*
433 copy_app_domain_setup (MonoDomain *domain, MonoAppDomainSetup *setup)
435 MonoDomain *caller_domain = mono_domain_get ();
436 MonoClass *ads_class = mono_class_from_name (mono_defaults.corlib, "System", "AppDomainSetup");
437 MonoAppDomainSetup *copy = (MonoAppDomainSetup*)mono_object_new (domain, ads_class);
439 mono_domain_set_internal (domain);
441 MONO_OBJECT_SETREF (copy, application_base, mono_marshal_xdomain_copy_value ((MonoObject*)setup->application_base));
442 MONO_OBJECT_SETREF (copy, application_name, mono_marshal_xdomain_copy_value ((MonoObject*)setup->application_name));
443 MONO_OBJECT_SETREF (copy, cache_path, mono_marshal_xdomain_copy_value ((MonoObject*)setup->cache_path));
444 MONO_OBJECT_SETREF (copy, configuration_file, mono_marshal_xdomain_copy_value ((MonoObject*)setup->configuration_file));
445 MONO_OBJECT_SETREF (copy, dynamic_base, mono_marshal_xdomain_copy_value ((MonoObject*)setup->dynamic_base));
446 MONO_OBJECT_SETREF (copy, license_file, mono_marshal_xdomain_copy_value ((MonoObject*)setup->license_file));
447 MONO_OBJECT_SETREF (copy, private_bin_path, mono_marshal_xdomain_copy_value ((MonoObject*)setup->private_bin_path));
448 MONO_OBJECT_SETREF (copy, private_bin_path_probe, mono_marshal_xdomain_copy_value ((MonoObject*)setup->private_bin_path_probe));
449 MONO_OBJECT_SETREF (copy, shadow_copy_directories, mono_marshal_xdomain_copy_value ((MonoObject*)setup->shadow_copy_directories));
450 MONO_OBJECT_SETREF (copy, shadow_copy_files, mono_marshal_xdomain_copy_value ((MonoObject*)setup->shadow_copy_files));
451 copy->publisher_policy = setup->publisher_policy;
452 copy->path_changed = setup->path_changed;
453 copy->loader_optimization = setup->loader_optimization;
454 copy->disallow_binding_redirects = setup->disallow_binding_redirects;
455 copy->disallow_code_downloads = setup->disallow_code_downloads;
456 MONO_OBJECT_SETREF (copy, domain_initializer_args, mono_marshal_xdomain_copy_value ((MonoObject*)setup->domain_initializer_args));
457 copy->disallow_appbase_probe = setup->disallow_appbase_probe;
458 MONO_OBJECT_SETREF (copy, application_trust, mono_marshal_xdomain_copy_value ((MonoObject*)setup->application_trust));
459 MONO_OBJECT_SETREF (copy, configuration_bytes, mono_marshal_xdomain_copy_value ((MonoObject*)setup->configuration_bytes));
460 MONO_OBJECT_SETREF (copy, serialized_non_primitives, mono_marshal_xdomain_copy_value ((MonoObject*)setup->serialized_non_primitives));
462 mono_domain_set_internal (caller_domain);
464 return copy;
467 static MonoAppDomain *
468 mono_domain_create_appdomain_internal (char *friendly_name, MonoAppDomainSetup *setup)
470 MonoError error;
471 MonoClass *adclass;
472 MonoAppDomain *ad;
473 MonoDomain *data;
474 char *shadow_location;
476 adclass = mono_class_from_name (mono_defaults.corlib, "System", "AppDomain");
478 /* FIXME: pin all those objects */
479 data = mono_domain_create();
481 ad = (MonoAppDomain *) mono_object_new (data, adclass);
482 ad->data = data;
483 data->domain = ad;
484 data->friendly_name = g_strdup (friendly_name);
486 if (!setup->application_base) {
487 /* Inherit from the root domain since MS.NET does this */
488 MonoDomain *root = mono_get_root_domain ();
489 if (root->setup->application_base)
490 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)));
493 mono_context_init (data);
495 data->setup = copy_app_domain_setup (data, setup);
496 mono_set_private_bin_path_from_config (data);
497 add_assemblies_to_domain (data, mono_defaults.corlib->assembly, NULL);
499 #ifndef DISABLE_SHADOW_COPY
500 /*FIXME, guard this for when the debugger is not running */
501 shadow_location = get_shadow_assembly_location_base (data, &error);
502 if (!mono_error_ok (&error))
503 mono_error_raise_exception (&error);
504 g_free (shadow_location);
505 #endif
507 create_domain_objects (data);
509 return ad;
513 * mono_domain_has_type_resolve:
514 * @domain: application domains being looked up
516 * Returns true if the AppDomain.TypeResolve field has been
517 * set.
519 gboolean
520 mono_domain_has_type_resolve (MonoDomain *domain)
522 static MonoClassField *field = NULL;
523 MonoObject *o;
525 if (field == NULL) {
526 field = mono_class_get_field_from_name (mono_defaults.appdomain_class, "TypeResolve");
527 g_assert (field);
530 /*pedump doesn't create an appdomin, so the domain object doesn't exist.*/
531 if (!domain->domain)
532 return FALSE;
534 mono_field_get_value ((MonoObject*)(domain->domain), field, &o);
535 return o != NULL;
539 * mono_domain_try_type_resolve:
540 * @domain: application domainwhere the name where the type is going to be resolved
541 * @name: the name of the type to resolve or NULL.
542 * @tb: A System.Reflection.Emit.TypeBuilder, used if name is NULL.
544 * This routine invokes the internal System.AppDomain.DoTypeResolve and returns
545 * the assembly that matches name.
547 * If @name is null, the value of ((TypeBuilder)tb).FullName is used instead
549 * Returns: A MonoReflectionAssembly or NULL if not found
551 MonoReflectionAssembly *
552 mono_domain_try_type_resolve (MonoDomain *domain, char *name, MonoObject *tb)
554 MonoClass *klass;
555 void *params [1];
556 static MonoMethod *method = NULL;
558 g_assert (domain != NULL && ((name != NULL) || (tb != NULL)));
560 if (method == NULL) {
561 klass = domain->domain->mbr.obj.vtable->klass;
562 g_assert (klass);
564 method = mono_class_get_method_from_name (klass, "DoTypeResolve", -1);
565 if (method == NULL) {
566 g_warning ("Method AppDomain.DoTypeResolve not found.\n");
567 return NULL;
571 if (name)
572 *params = (MonoObject*)mono_string_new (mono_domain_get (), name);
573 else
574 *params = tb;
575 return (MonoReflectionAssembly *) mono_runtime_invoke (method, domain->domain, params, NULL);
579 * mono_domain_owns_vtable_slot:
581 * Returns whenever VTABLE_SLOT is inside a vtable which belongs to DOMAIN.
583 gboolean
584 mono_domain_owns_vtable_slot (MonoDomain *domain, gpointer vtable_slot)
586 gboolean res;
588 mono_domain_lock (domain);
589 res = mono_mempool_contains_addr (domain->mp, vtable_slot);
590 mono_domain_unlock (domain);
591 return res;
595 * mono_domain_set:
596 * @domain: domain
597 * @force: force setting.
599 * Set the current appdomain to @domain. If @force is set, set it even
600 * if it is being unloaded.
602 * Returns:
603 * TRUE on success;
604 * FALSE if the domain is unloaded
606 gboolean
607 mono_domain_set (MonoDomain *domain, gboolean force)
609 if (!force && domain->state == MONO_APPDOMAIN_UNLOADED)
610 return FALSE;
612 mono_domain_set_internal (domain);
614 return TRUE;
617 MonoObject *
618 ves_icall_System_AppDomain_GetData (MonoAppDomain *ad, MonoString *name)
620 MonoDomain *add;
621 MonoObject *o;
622 char *str;
624 MONO_CHECK_ARG_NULL (name, NULL);
626 g_assert (ad != NULL);
627 add = ad->data;
628 g_assert (add != NULL);
630 str = mono_string_to_utf8 (name);
632 mono_domain_lock (add);
634 if (!strcmp (str, "APPBASE"))
635 o = (MonoObject *)add->setup->application_base;
636 else if (!strcmp (str, "APP_CONFIG_FILE"))
637 o = (MonoObject *)add->setup->configuration_file;
638 else if (!strcmp (str, "DYNAMIC_BASE"))
639 o = (MonoObject *)add->setup->dynamic_base;
640 else if (!strcmp (str, "APP_NAME"))
641 o = (MonoObject *)add->setup->application_name;
642 else if (!strcmp (str, "CACHE_BASE"))
643 o = (MonoObject *)add->setup->cache_path;
644 else if (!strcmp (str, "PRIVATE_BINPATH"))
645 o = (MonoObject *)add->setup->private_bin_path;
646 else if (!strcmp (str, "BINPATH_PROBE_ONLY"))
647 o = (MonoObject *)add->setup->private_bin_path_probe;
648 else if (!strcmp (str, "SHADOW_COPY_DIRS"))
649 o = (MonoObject *)add->setup->shadow_copy_directories;
650 else if (!strcmp (str, "FORCE_CACHE_INSTALL"))
651 o = (MonoObject *)add->setup->shadow_copy_files;
652 else
653 o = mono_g_hash_table_lookup (add->env, name);
655 mono_domain_unlock (add);
656 g_free (str);
658 if (!o)
659 return NULL;
661 return o;
664 void
665 ves_icall_System_AppDomain_SetData (MonoAppDomain *ad, MonoString *name, MonoObject *data)
667 MonoDomain *add;
669 MONO_CHECK_ARG_NULL (name,);
671 g_assert (ad != NULL);
672 add = ad->data;
673 g_assert (add != NULL);
675 mono_domain_lock (add);
677 mono_g_hash_table_insert (add->env, name, data);
679 mono_domain_unlock (add);
682 MonoAppDomainSetup *
683 ves_icall_System_AppDomain_getSetup (MonoAppDomain *ad)
685 g_assert (ad != NULL);
686 g_assert (ad->data != NULL);
688 return ad->data->setup;
691 MonoString *
692 ves_icall_System_AppDomain_getFriendlyName (MonoAppDomain *ad)
694 g_assert (ad != NULL);
695 g_assert (ad->data != NULL);
697 return mono_string_new (ad->data, ad->data->friendly_name);
700 MonoAppDomain *
701 ves_icall_System_AppDomain_getCurDomain ()
703 MonoDomain *add = mono_domain_get ();
705 return add->domain;
708 MonoAppDomain *
709 ves_icall_System_AppDomain_getRootDomain ()
711 MonoDomain *root = mono_get_root_domain ();
713 return root->domain;
716 static char*
717 get_attribute_value (const gchar **attribute_names,
718 const gchar **attribute_values,
719 const char *att_name)
721 int n;
722 for (n = 0; attribute_names [n] != NULL; n++) {
723 if (strcmp (attribute_names [n], att_name) == 0)
724 return g_strdup (attribute_values [n]);
726 return NULL;
729 static void
730 start_element (GMarkupParseContext *context,
731 const gchar *element_name,
732 const gchar **attribute_names,
733 const gchar **attribute_values,
734 gpointer user_data,
735 GError **error)
737 RuntimeConfig *runtime_config = user_data;
739 if (strcmp (element_name, "runtime") == 0) {
740 runtime_config->runtime_count++;
741 return;
744 if (strcmp (element_name, "assemblyBinding") == 0) {
745 runtime_config->assemblybinding_count++;
746 return;
749 if (runtime_config->runtime_count != 1 || runtime_config->assemblybinding_count != 1)
750 return;
752 if (strcmp (element_name, "probing") != 0)
753 return;
755 g_free (runtime_config->domain->private_bin_path);
756 runtime_config->domain->private_bin_path = get_attribute_value (attribute_names, attribute_values, "privatePath");
757 if (runtime_config->domain->private_bin_path && !runtime_config->domain->private_bin_path [0]) {
758 g_free (runtime_config->domain->private_bin_path);
759 runtime_config->domain->private_bin_path = NULL;
760 return;
764 static void
765 end_element (GMarkupParseContext *context,
766 const gchar *element_name,
767 gpointer user_data,
768 GError **error)
770 RuntimeConfig *runtime_config = user_data;
771 if (strcmp (element_name, "runtime") == 0)
772 runtime_config->runtime_count--;
773 else if (strcmp (element_name, "assemblyBinding") == 0)
774 runtime_config->assemblybinding_count--;
777 static void
778 parse_error (GMarkupParseContext *context, GError *error, gpointer user_data)
780 RuntimeConfig *state = user_data;
781 const gchar *msg;
782 const gchar *filename;
784 filename = state && state->filename ? (gchar *) state->filename : "<unknown>";
785 msg = error && error->message ? error->message : "";
786 g_warning ("Error parsing %s: %s", filename, msg);
789 static const GMarkupParser
790 mono_parser = {
791 start_element,
792 end_element,
793 NULL,
794 NULL,
795 parse_error
798 void
799 mono_set_private_bin_path_from_config (MonoDomain *domain)
801 MonoError error;
802 gchar *config_file_name = NULL, *text = NULL, *config_file_path = NULL;
803 gsize len;
804 GMarkupParseContext *context;
805 RuntimeConfig runtime_config;
806 gint offset;
808 if (!domain || !domain->setup || !domain->setup->configuration_file)
809 return;
811 config_file_name = mono_string_to_utf8_checked (domain->setup->configuration_file, &error);
812 if (!mono_error_ok (&error)) {
813 mono_error_cleanup (&error);
814 goto free_and_out;
817 config_file_path = mono_portability_find_file (config_file_name, TRUE);
818 if (!config_file_path)
819 config_file_path = config_file_name;
821 if (!g_file_get_contents (config_file_path, &text, &len, NULL))
822 goto free_and_out;
824 runtime_config.runtime_count = 0;
825 runtime_config.assemblybinding_count = 0;
826 runtime_config.domain = domain;
827 runtime_config.filename = config_file_path;
829 offset = 0;
830 if (len > 3 && text [0] == '\xef' && text [1] == (gchar) '\xbb' && text [2] == '\xbf')
831 offset = 3; /* Skip UTF-8 BOM */
833 context = g_markup_parse_context_new (&mono_parser, 0, &runtime_config, NULL);
834 if (g_markup_parse_context_parse (context, text + offset, len - offset, NULL))
835 g_markup_parse_context_end_parse (context, NULL);
836 g_markup_parse_context_free (context);
838 free_and_out:
839 g_free (text);
840 if (config_file_name != config_file_path)
841 g_free (config_file_name);
842 g_free (config_file_path);
845 MonoAppDomain *
846 ves_icall_System_AppDomain_createDomain (MonoString *friendly_name, MonoAppDomainSetup *setup)
848 #ifdef DISABLE_APPDOMAINS
849 mono_set_pending_exception (mono_get_exception_not_supported ("AppDomain creation is not supported on this runtime."));
850 return NULL;
851 #else
852 char *fname = mono_string_to_utf8 (friendly_name);
853 MonoAppDomain *ad = mono_domain_create_appdomain_internal (fname, setup);
855 g_free (fname);
857 return ad;
858 #endif
861 MonoArray *
862 ves_icall_System_AppDomain_GetAssemblies (MonoAppDomain *ad, MonoBoolean refonly)
864 MonoDomain *domain = ad->data;
865 MonoAssembly* ass;
866 static MonoClass *System_Reflection_Assembly;
867 MonoArray *res;
868 GSList *tmp;
869 int i;
870 GPtrArray *assemblies;
872 if (!System_Reflection_Assembly)
873 System_Reflection_Assembly = mono_class_from_name (
874 mono_defaults.corlib, "System.Reflection", "Assembly");
877 * Make a copy of the list of assemblies because we can't hold the assemblies
878 * lock while creating objects etc.
880 assemblies = g_ptr_array_new ();
881 /* Need to skip internal assembly builders created by remoting */
882 mono_domain_assemblies_lock (domain);
883 for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) {
884 ass = tmp->data;
885 if (refonly != ass->ref_only)
886 continue;
887 if (ass->corlib_internal)
888 continue;
889 g_ptr_array_add (assemblies, ass);
891 mono_domain_assemblies_unlock (domain);
893 res = mono_array_new (domain, System_Reflection_Assembly, assemblies->len);
894 for (i = 0; i < assemblies->len; ++i) {
895 ass = g_ptr_array_index (assemblies, i);
896 mono_array_setref (res, i, mono_assembly_get_object (domain, ass));
899 g_ptr_array_free (assemblies, TRUE);
901 return res;
904 MonoReflectionAssembly *
905 mono_try_assembly_resolve (MonoDomain *domain, MonoString *fname, MonoAssembly *requesting, gboolean refonly)
907 MonoClass *klass;
908 MonoMethod *method;
909 MonoBoolean isrefonly;
910 gpointer params [3];
912 if (mono_runtime_get_no_exec ())
913 return NULL;
915 g_assert (domain != NULL && fname != NULL);
917 klass = domain->domain->mbr.obj.vtable->klass;
918 g_assert (klass);
920 method = mono_class_get_method_from_name (klass, "DoAssemblyResolve", -1);
921 if (method == NULL) {
922 g_warning ("Method AppDomain.DoAssemblyResolve not found.\n");
923 return NULL;
926 isrefonly = refonly ? 1 : 0;
927 params [0] = fname;
928 params [1] = (requesting) ? mono_assembly_get_object (domain, requesting) : NULL;
929 params [2] = &isrefonly;
930 return (MonoReflectionAssembly *) mono_runtime_invoke (method, domain->domain, params, NULL);
933 MonoAssembly *
934 mono_domain_assembly_postload_search (MonoAssemblyName *aname, MonoAssembly *requesting,
935 gboolean refonly)
937 MonoReflectionAssembly *assembly;
938 MonoDomain *domain = mono_domain_get ();
939 char *aname_str;
940 MonoString *str;
942 aname_str = mono_stringify_assembly_name (aname);
944 /* FIXME: We invoke managed code here, so there is a potential for deadlocks */
945 str = mono_string_new (domain, aname_str);
946 if (!str) {
947 g_free (aname_str);
948 return NULL;
950 assembly = mono_try_assembly_resolve (domain, str, requesting, refonly);
951 g_free (aname_str);
953 if (assembly)
954 return assembly->assembly;
955 else
956 return NULL;
960 * LOCKING: assumes assemblies_lock in the domain is already locked.
962 static void
963 add_assemblies_to_domain (MonoDomain *domain, MonoAssembly *ass, GHashTable *ht)
965 gint i;
966 GSList *tmp;
967 gboolean destroy_ht = FALSE;
969 if (!ass->aname.name)
970 return;
972 if (!ht) {
973 ht = g_hash_table_new (mono_aligned_addr_hash, NULL);
974 destroy_ht = TRUE;
977 /* FIXME: handle lazy loaded assemblies */
978 for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) {
979 g_hash_table_insert (ht, tmp->data, tmp->data);
981 if (!g_hash_table_lookup (ht, ass)) {
982 mono_assembly_addref (ass);
983 g_hash_table_insert (ht, ass, ass);
984 domain->domain_assemblies = g_slist_append (domain->domain_assemblies, ass);
985 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Assembly %s[%p] added to domain %s, ref_count=%d", ass->aname.name, ass, domain->friendly_name, ass->ref_count);
988 if (ass->image->references) {
989 for (i = 0; ass->image->references [i] != NULL; i++) {
990 if (ass->image->references [i] != REFERENCE_MISSING)
991 if (!g_hash_table_lookup (ht, ass->image->references [i])) {
992 add_assemblies_to_domain (domain, ass->image->references [i], ht);
996 if (destroy_ht)
997 g_hash_table_destroy (ht);
1000 static void
1001 mono_domain_fire_assembly_load (MonoAssembly *assembly, gpointer user_data)
1003 static MonoClassField *assembly_load_field;
1004 static MonoMethod *assembly_load_method;
1005 MonoDomain *domain = mono_domain_get ();
1006 MonoReflectionAssembly *ref_assembly;
1007 MonoClass *klass;
1008 gpointer load_value;
1009 void *params [1];
1011 if (!domain->domain)
1012 /* This can happen during startup */
1013 return;
1014 #ifdef ASSEMBLY_LOAD_DEBUG
1015 fprintf (stderr, "Loading %s into domain %s\n", assembly->aname.name, domain->friendly_name);
1016 #endif
1017 klass = domain->domain->mbr.obj.vtable->klass;
1019 mono_domain_assemblies_lock (domain);
1020 add_assemblies_to_domain (domain, assembly, NULL);
1021 mono_domain_assemblies_unlock (domain);
1023 if (assembly_load_field == NULL) {
1024 assembly_load_field = mono_class_get_field_from_name (klass, "AssemblyLoad");
1025 g_assert (assembly_load_field);
1028 mono_field_get_value ((MonoObject*) domain->domain, assembly_load_field, &load_value);
1029 if (load_value == NULL) {
1030 /* No events waiting to be triggered */
1031 return;
1034 ref_assembly = mono_assembly_get_object (domain, assembly);
1035 g_assert (ref_assembly);
1037 if (assembly_load_method == NULL) {
1038 assembly_load_method = mono_class_get_method_from_name (klass, "DoAssemblyLoad", -1);
1039 g_assert (assembly_load_method);
1042 *params = ref_assembly;
1043 mono_runtime_invoke (assembly_load_method, domain->domain, params, NULL);
1047 * LOCKING: Acquires the domain assemblies lock.
1049 static void
1050 set_domain_search_path (MonoDomain *domain)
1052 MonoError error;
1053 MonoAppDomainSetup *setup;
1054 gchar **tmp;
1055 gchar *search_path = NULL;
1056 gint i;
1057 gint npaths = 0;
1058 gchar **pvt_split = NULL;
1059 GError *gerror = NULL;
1060 gint appbaselen = -1;
1063 * We use the low-level domain assemblies lock, since this is called from
1064 * assembly loads hooks, which means this thread might hold the loader lock.
1066 mono_domain_assemblies_lock (domain);
1068 if (!domain->setup) {
1069 mono_domain_assemblies_unlock (domain);
1070 return;
1073 if ((domain->search_path != NULL) && !domain->setup->path_changed) {
1074 mono_domain_assemblies_unlock (domain);
1075 return;
1077 setup = domain->setup;
1078 if (!setup->application_base) {
1079 mono_domain_assemblies_unlock (domain);
1080 return; /* Must set application base to get private path working */
1083 npaths++;
1085 if (setup->private_bin_path) {
1086 search_path = mono_string_to_utf8_checked (setup->private_bin_path, &error);
1087 if (!mono_error_ok (&error)) { /*FIXME maybe we should bubble up the error.*/
1088 g_warning ("Could not decode AppDomain search path since it contains invalid characters");
1089 mono_error_cleanup (&error);
1090 mono_domain_assemblies_unlock (domain);
1091 return;
1095 if (domain->private_bin_path) {
1096 if (search_path == NULL)
1097 search_path = domain->private_bin_path;
1098 else {
1099 gchar *tmp2 = search_path;
1100 search_path = g_strjoin (";", search_path, domain->private_bin_path, NULL);
1101 g_free (tmp2);
1105 if (search_path) {
1107 * As per MSDN documentation, AppDomainSetup.PrivateBinPath contains a list of
1108 * directories relative to ApplicationBase separated by semicolons (see
1109 * http://msdn2.microsoft.com/en-us/library/system.appdomainsetup.privatebinpath.aspx)
1110 * The loop below copes with the fact that some Unix applications may use ':' (or
1111 * System.IO.Path.PathSeparator) as the path search separator. We replace it with
1112 * ';' for the subsequent split.
1114 * The issue was reported in bug #81446
1117 #ifndef TARGET_WIN32
1118 gint slen;
1120 slen = strlen (search_path);
1121 for (i = 0; i < slen; i++)
1122 if (search_path [i] == ':')
1123 search_path [i] = ';';
1124 #endif
1126 pvt_split = g_strsplit (search_path, ";", 1000);
1127 g_free (search_path);
1128 for (tmp = pvt_split; *tmp; tmp++, npaths++);
1131 if (!npaths) {
1132 if (pvt_split)
1133 g_strfreev (pvt_split);
1135 * Don't do this because the first time is called, the domain
1136 * setup is not finished.
1138 * domain->search_path = g_malloc (sizeof (char *));
1139 * domain->search_path [0] = NULL;
1141 mono_domain_assemblies_unlock (domain);
1142 return;
1145 if (domain->search_path)
1146 g_strfreev (domain->search_path);
1148 tmp = g_malloc ((npaths + 1) * sizeof (gchar *));
1149 tmp [npaths] = NULL;
1151 *tmp = mono_string_to_utf8_checked (setup->application_base, &error);
1152 if (!mono_error_ok (&error)) {
1153 mono_error_cleanup (&error);
1154 g_strfreev (pvt_split);
1155 g_free (tmp);
1157 mono_domain_assemblies_unlock (domain);
1158 return;
1161 domain->search_path = tmp;
1163 /* FIXME: is this needed? */
1164 if (strncmp (*tmp, "file://", 7) == 0) {
1165 gchar *file = *tmp;
1166 gchar *uri = *tmp;
1167 gchar *tmpuri;
1169 if (uri [7] != '/')
1170 uri = g_strdup_printf ("file:///%s", uri + 7);
1172 tmpuri = uri;
1173 uri = mono_escape_uri_string (tmpuri);
1174 *tmp = g_filename_from_uri (uri, NULL, &gerror);
1175 g_free (uri);
1177 if (tmpuri != file)
1178 g_free (tmpuri);
1180 if (gerror != NULL) {
1181 g_warning ("%s\n", gerror->message);
1182 g_error_free (gerror);
1183 *tmp = file;
1184 } else {
1185 g_free (file);
1189 for (i = 1; pvt_split && i < npaths; i++) {
1190 if (g_path_is_absolute (pvt_split [i - 1])) {
1191 tmp [i] = g_strdup (pvt_split [i - 1]);
1192 } else {
1193 tmp [i] = g_build_filename (tmp [0], pvt_split [i - 1], NULL);
1196 if (strchr (tmp [i], '.')) {
1197 gchar *reduced;
1198 gchar *freeme;
1200 reduced = mono_path_canonicalize (tmp [i]);
1201 if (appbaselen == -1)
1202 appbaselen = strlen (tmp [0]);
1204 if (strncmp (tmp [0], reduced, appbaselen)) {
1205 g_free (reduced);
1206 g_free (tmp [i]);
1207 tmp [i] = g_strdup ("");
1208 continue;
1211 freeme = tmp [i];
1212 tmp [i] = reduced;
1213 g_free (freeme);
1217 if (setup->private_bin_path_probe != NULL) {
1218 g_free (tmp [0]);
1219 tmp [0] = g_strdup ("");
1222 domain->setup->path_changed = FALSE;
1224 g_strfreev (pvt_split);
1226 mono_domain_assemblies_unlock (domain);
1229 #ifdef DISABLE_SHADOW_COPY
1230 gboolean
1231 mono_is_shadow_copy_enabled (MonoDomain *domain, const gchar *dir_name)
1233 return FALSE;
1236 char *
1237 mono_make_shadow_copy (const char *filename)
1239 return (char *) filename;
1241 #else
1242 static gboolean
1243 shadow_copy_sibling (gchar *src, gint srclen, const char *extension, gchar *target, gint targetlen, gint tail_len)
1245 guint16 *orig, *dest;
1246 gboolean copy_result;
1248 strcpy (src + srclen - tail_len, extension);
1250 if (IS_PORTABILITY_CASE) {
1251 gchar *file = mono_portability_find_file (src, TRUE);
1253 if (file == NULL)
1254 return TRUE;
1256 g_free (file);
1257 } else if (!g_file_test (src, G_FILE_TEST_IS_REGULAR)) {
1258 return TRUE;
1261 orig = g_utf8_to_utf16 (src, strlen (src), NULL, NULL, NULL);
1263 strcpy (target + targetlen - tail_len, extension);
1264 dest = g_utf8_to_utf16 (target, strlen (target), NULL, NULL, NULL);
1266 DeleteFile (dest);
1267 copy_result = CopyFile (orig, dest, FALSE);
1269 /* Fix for bug #556884 - make sure the files have the correct mode so that they can be
1270 * overwritten when updated in their original locations. */
1271 if (copy_result)
1272 copy_result = SetFileAttributes (dest, FILE_ATTRIBUTE_NORMAL);
1274 g_free (orig);
1275 g_free (dest);
1277 return copy_result;
1280 static gint32
1281 get_cstring_hash (const char *str)
1283 int len, i;
1284 const char *p;
1285 gint32 h = 0;
1287 if (!str || !str [0])
1288 return 0;
1290 len = strlen (str);
1291 p = str;
1292 for (i = 0; i < len; i++) {
1293 h = (h << 5) - h + *p;
1294 p++;
1297 return h;
1301 * Returned memory is malloc'd. Called must free it
1303 static char *
1304 get_shadow_assembly_location_base (MonoDomain *domain, MonoError *error)
1306 MonoAppDomainSetup *setup;
1307 char *cache_path, *appname;
1308 char *userdir;
1309 char *location;
1311 mono_error_init (error);
1313 setup = domain->setup;
1314 if (setup->cache_path != NULL && setup->application_name != NULL) {
1315 cache_path = mono_string_to_utf8_checked (setup->cache_path, error);
1316 if (!mono_error_ok (error))
1317 return NULL;
1318 #ifndef TARGET_WIN32
1320 gint i;
1321 for (i = strlen (cache_path) - 1; i >= 0; i--)
1322 if (cache_path [i] == '\\')
1323 cache_path [i] = '/';
1325 #endif
1327 appname = mono_string_to_utf8_checked (setup->application_name, error);
1328 if (!mono_error_ok (error)) {
1329 g_free (cache_path);
1330 return NULL;
1333 location = g_build_filename (cache_path, appname, "assembly", "shadow", NULL);
1334 g_free (appname);
1335 g_free (cache_path);
1336 } else {
1337 userdir = g_strdup_printf ("%s-mono-cachepath", g_get_user_name ());
1338 location = g_build_filename (g_get_tmp_dir (), userdir, "assembly", "shadow", NULL);
1339 g_free (userdir);
1341 return location;
1344 static char *
1345 get_shadow_assembly_location (const char *filename, MonoError *error)
1347 gint32 hash = 0, hash2 = 0;
1348 char name_hash [9];
1349 char path_hash [30];
1350 char *bname = g_path_get_basename (filename);
1351 char *dirname = g_path_get_dirname (filename);
1352 char *location, *tmploc;
1353 MonoDomain *domain = mono_domain_get ();
1355 mono_error_init (error);
1357 hash = get_cstring_hash (bname);
1358 hash2 = get_cstring_hash (dirname);
1359 g_snprintf (name_hash, sizeof (name_hash), "%08x", hash);
1360 g_snprintf (path_hash, sizeof (path_hash), "%08x_%08x_%08x", hash ^ hash2, hash2, domain->shadow_serial);
1361 tmploc = get_shadow_assembly_location_base (domain, error);
1362 if (!mono_error_ok (error)) {
1363 g_free (bname);
1364 g_free (dirname);
1365 return NULL;
1368 location = g_build_filename (tmploc, name_hash, path_hash, bname, NULL);
1369 g_free (tmploc);
1370 g_free (bname);
1371 g_free (dirname);
1372 return location;
1375 static gboolean
1376 ensure_directory_exists (const char *filename)
1378 #ifdef HOST_WIN32
1379 gchar *dir_utf8 = g_path_get_dirname (filename);
1380 gunichar2 *p;
1381 gunichar2 *dir_utf16 = NULL;
1382 int retval;
1384 if (!dir_utf8 || !dir_utf8 [0])
1385 return FALSE;
1387 dir_utf16 = g_utf8_to_utf16 (dir_utf8, strlen (dir_utf8), NULL, NULL, NULL);
1388 g_free (dir_utf8);
1390 if (!dir_utf16)
1391 return FALSE;
1393 p = dir_utf16;
1395 /* make life easy and only use one directory seperator */
1396 while (*p != '\0')
1398 if (*p == '/')
1399 *p = '\\';
1400 p++;
1403 p = dir_utf16;
1405 /* get past C:\ )*/
1406 while (*p++ != '\\')
1410 while (1) {
1411 BOOL bRet = FALSE;
1412 p = wcschr (p, '\\');
1413 if (p)
1414 *p = '\0';
1415 retval = _wmkdir (dir_utf16);
1416 if (retval != 0 && errno != EEXIST) {
1417 g_free (dir_utf16);
1418 return FALSE;
1420 if (!p)
1421 break;
1422 *p++ = '\\';
1425 g_free (dir_utf16);
1426 return TRUE;
1427 #else
1428 char *p;
1429 gchar *dir = g_path_get_dirname (filename);
1430 int retval;
1431 struct stat sbuf;
1433 if (!dir || !dir [0]) {
1434 g_free (dir);
1435 return FALSE;
1438 if (stat (dir, &sbuf) == 0 && S_ISDIR (sbuf.st_mode)) {
1439 g_free (dir);
1440 return TRUE;
1443 p = dir;
1444 while (*p == '/')
1445 p++;
1447 while (1) {
1448 p = strchr (p, '/');
1449 if (p)
1450 *p = '\0';
1451 retval = mkdir (dir, 0777);
1452 if (retval != 0 && errno != EEXIST) {
1453 g_free (dir);
1454 return FALSE;
1456 if (!p)
1457 break;
1458 *p++ = '/';
1461 g_free (dir);
1462 return TRUE;
1463 #endif
1466 static gboolean
1467 private_file_needs_copying (const char *src, struct stat *sbuf_src, char *dest)
1469 struct stat sbuf_dest;
1470 gchar *stat_src;
1471 gchar *real_src = mono_portability_find_file (src, TRUE);
1473 if (!real_src)
1474 stat_src = (gchar*)src;
1475 else
1476 stat_src = real_src;
1478 if (stat (stat_src, sbuf_src) == -1) {
1479 time_t tnow = time (NULL);
1481 if (real_src)
1482 g_free (real_src);
1484 memset (sbuf_src, 0, sizeof (*sbuf_src));
1485 sbuf_src->st_mtime = tnow;
1486 sbuf_src->st_atime = tnow;
1487 return TRUE;
1490 if (real_src)
1491 g_free (real_src);
1493 if (stat (dest, &sbuf_dest) == -1)
1494 return TRUE;
1496 if (sbuf_src->st_size == sbuf_dest.st_size &&
1497 sbuf_src->st_mtime == sbuf_dest.st_mtime)
1498 return FALSE;
1500 return TRUE;
1503 static gboolean
1504 shadow_copy_create_ini (const char *shadow, const char *filename)
1506 char *dir_name;
1507 char *ini_file;
1508 guint16 *u16_ini;
1509 gboolean result;
1510 guint32 n;
1511 HANDLE *handle;
1512 gchar *full_path;
1514 dir_name = g_path_get_dirname (shadow);
1515 ini_file = g_build_filename (dir_name, "__AssemblyInfo__.ini", NULL);
1516 g_free (dir_name);
1517 if (g_file_test (ini_file, G_FILE_TEST_IS_REGULAR)) {
1518 g_free (ini_file);
1519 return TRUE;
1522 u16_ini = g_utf8_to_utf16 (ini_file, strlen (ini_file), NULL, NULL, NULL);
1523 g_free (ini_file);
1524 if (!u16_ini) {
1525 return FALSE;
1527 handle = CreateFile (u16_ini, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
1528 NULL, CREATE_NEW, FileAttributes_Normal, NULL);
1529 g_free (u16_ini);
1530 if (handle == INVALID_HANDLE_VALUE) {
1531 return FALSE;
1534 full_path = mono_path_resolve_symlinks (filename);
1535 result = WriteFile (handle, full_path, strlen (full_path), &n, NULL);
1536 g_free (full_path);
1537 CloseHandle (handle);
1538 return result;
1541 gboolean
1542 mono_is_shadow_copy_enabled (MonoDomain *domain, const gchar *dir_name)
1544 MonoError error;
1545 MonoAppDomainSetup *setup;
1546 gchar *all_dirs;
1547 gchar **dir_ptr;
1548 gchar **directories;
1549 gchar *shadow_status_string;
1550 gchar *base_dir;
1551 gboolean shadow_enabled;
1552 gboolean found = FALSE;
1554 if (domain == NULL)
1555 return FALSE;
1557 setup = domain->setup;
1558 if (setup == NULL || setup->shadow_copy_files == NULL)
1559 return FALSE;
1561 shadow_status_string = mono_string_to_utf8_checked (setup->shadow_copy_files, &error);
1562 if (!mono_error_ok (&error)) {
1563 mono_error_cleanup (&error);
1564 return FALSE;
1566 shadow_enabled = !g_ascii_strncasecmp (shadow_status_string, "true", 4);
1567 g_free (shadow_status_string);
1569 if (!shadow_enabled)
1570 return FALSE;
1572 if (setup->shadow_copy_directories == NULL)
1573 return TRUE;
1575 /* Is dir_name a shadow_copy destination already? */
1576 base_dir = get_shadow_assembly_location_base (domain, &error);
1577 if (!mono_error_ok (&error)) {
1578 mono_error_cleanup (&error);
1579 return FALSE;
1582 if (strstr (dir_name, base_dir)) {
1583 g_free (base_dir);
1584 return TRUE;
1586 g_free (base_dir);
1588 all_dirs = mono_string_to_utf8_checked (setup->shadow_copy_directories, &error);
1589 if (!mono_error_ok (&error)) {
1590 mono_error_cleanup (&error);
1591 return FALSE;
1594 directories = g_strsplit (all_dirs, G_SEARCHPATH_SEPARATOR_S, 1000);
1595 dir_ptr = directories;
1596 while (*dir_ptr) {
1597 if (**dir_ptr != '\0' && !strcmp (*dir_ptr, dir_name)) {
1598 found = TRUE;
1599 break;
1601 dir_ptr++;
1603 g_strfreev (directories);
1604 g_free (all_dirs);
1605 return found;
1609 This function raises exceptions so it can cause as sorts of nasty stuff if called
1610 while holding a lock.
1611 Returns old file name if shadow copy is disabled, new shadow copy file name if successful
1612 or NULL if source file not found.
1613 FIXME bubble up the error instead of raising it here
1615 char *
1616 mono_make_shadow_copy (const char *filename)
1618 MonoError error;
1619 gchar *sibling_source, *sibling_target;
1620 gint sibling_source_len, sibling_target_len;
1621 guint16 *orig, *dest;
1622 guint32 attrs;
1623 char *shadow;
1624 gboolean copy_result;
1625 MonoException *exc;
1626 struct stat src_sbuf;
1627 struct utimbuf utbuf;
1628 char *dir_name = g_path_get_dirname (filename);
1629 MonoDomain *domain = mono_domain_get ();
1630 char *shadow_dir;
1632 set_domain_search_path (domain);
1634 if (!mono_is_shadow_copy_enabled (domain, dir_name)) {
1635 g_free (dir_name);
1636 return (char *) filename;
1639 /* Is dir_name a shadow_copy destination already? */
1640 shadow_dir = get_shadow_assembly_location_base (domain, &error);
1641 if (!mono_error_ok (&error)) {
1642 mono_error_cleanup (&error);
1643 g_free (dir_name);
1644 exc = mono_get_exception_execution_engine ("Failed to create shadow copy (invalid characters in shadow directory name).");
1645 mono_raise_exception (exc);
1648 if (strstr (dir_name, shadow_dir)) {
1649 g_free (shadow_dir);
1650 g_free (dir_name);
1651 return (char *) filename;
1653 g_free (shadow_dir);
1654 g_free (dir_name);
1656 shadow = get_shadow_assembly_location (filename, &error);
1657 if (!mono_error_ok (&error)) {
1658 mono_error_cleanup (&error);
1659 exc = mono_get_exception_execution_engine ("Failed to create shadow copy (invalid characters in file name).");
1660 mono_raise_exception (exc);
1663 if (ensure_directory_exists (shadow) == FALSE) {
1664 g_free (shadow);
1665 exc = mono_get_exception_execution_engine ("Failed to create shadow copy (ensure directory exists).");
1666 mono_raise_exception (exc);
1669 if (!private_file_needs_copying (filename, &src_sbuf, shadow))
1670 return (char*) shadow;
1672 orig = g_utf8_to_utf16 (filename, strlen (filename), NULL, NULL, NULL);
1673 dest = g_utf8_to_utf16 (shadow, strlen (shadow), NULL, NULL, NULL);
1674 DeleteFile (dest);
1676 /* Fix for bug #17066 - make sure we can read the file. if not then don't error but rather
1677 * let the assembly fail to load. This ensures you can do Type.GetType("NS.T, NonExistantAssembly)
1678 * and not have it runtime error" */
1679 attrs = GetFileAttributes (orig);
1680 if (attrs == INVALID_FILE_ATTRIBUTES) {
1681 g_free (shadow);
1682 return (char *)filename;
1685 copy_result = CopyFile (orig, dest, FALSE);
1687 /* Fix for bug #556884 - make sure the files have the correct mode so that they can be
1688 * overwritten when updated in their original locations. */
1689 if (copy_result)
1690 copy_result = SetFileAttributes (dest, FILE_ATTRIBUTE_NORMAL);
1692 g_free (dest);
1693 g_free (orig);
1695 if (copy_result == FALSE) {
1696 g_free (shadow);
1698 /* Fix for bug #17251 - if file not found try finding assembly by other means (it is not fatal error) */
1699 if (GetLastError() == ERROR_FILE_NOT_FOUND || GetLastError() == ERROR_PATH_NOT_FOUND)
1700 return NULL; /* file not found, shadow copy failed */
1702 exc = mono_get_exception_execution_engine ("Failed to create shadow copy (CopyFile).");
1703 mono_raise_exception (exc);
1706 /* attempt to copy .mdb, .config if they exist */
1707 sibling_source = g_strconcat (filename, ".config", NULL);
1708 sibling_source_len = strlen (sibling_source);
1709 sibling_target = g_strconcat (shadow, ".config", NULL);
1710 sibling_target_len = strlen (sibling_target);
1712 copy_result = shadow_copy_sibling (sibling_source, sibling_source_len, ".mdb", sibling_target, sibling_target_len, 7);
1713 if (copy_result == TRUE)
1714 copy_result = shadow_copy_sibling (sibling_source, sibling_source_len, ".config", sibling_target, sibling_target_len, 7);
1716 g_free (sibling_source);
1717 g_free (sibling_target);
1719 if (copy_result == FALSE) {
1720 g_free (shadow);
1721 exc = mono_get_exception_execution_engine ("Failed to create shadow copy of sibling data (CopyFile).");
1722 mono_raise_exception (exc);
1725 /* Create a .ini file containing the original assembly location */
1726 if (!shadow_copy_create_ini (shadow, filename)) {
1727 g_free (shadow);
1728 exc = mono_get_exception_execution_engine ("Failed to create shadow copy .ini file.");
1729 mono_raise_exception (exc);
1732 utbuf.actime = src_sbuf.st_atime;
1733 utbuf.modtime = src_sbuf.st_mtime;
1734 utime (shadow, &utbuf);
1736 return shadow;
1738 #endif /* DISABLE_SHADOW_COPY */
1740 MonoDomain *
1741 mono_domain_from_appdomain (MonoAppDomain *appdomain)
1743 if (appdomain == NULL)
1744 return NULL;
1746 return appdomain->data;
1749 static gboolean
1750 try_load_from (MonoAssembly **assembly, const gchar *path1, const gchar *path2,
1751 const gchar *path3, const gchar *path4,
1752 gboolean refonly, gboolean is_private)
1754 gchar *fullpath;
1755 gboolean found = FALSE;
1757 *assembly = NULL;
1758 fullpath = g_build_filename (path1, path2, path3, path4, NULL);
1760 if (IS_PORTABILITY_SET) {
1761 gchar *new_fullpath = mono_portability_find_file (fullpath, TRUE);
1762 if (new_fullpath) {
1763 g_free (fullpath);
1764 fullpath = new_fullpath;
1765 found = TRUE;
1767 } else
1768 found = g_file_test (fullpath, G_FILE_TEST_IS_REGULAR);
1770 if (found)
1771 *assembly = mono_assembly_open_full (fullpath, NULL, refonly);
1773 g_free (fullpath);
1774 return (*assembly != NULL);
1777 static MonoAssembly *
1778 real_load (gchar **search_path, const gchar *culture, const gchar *name, gboolean refonly)
1780 MonoAssembly *result = NULL;
1781 gchar **path;
1782 gchar *filename;
1783 const gchar *local_culture;
1784 gint len;
1785 gboolean is_private = FALSE;
1787 if (!culture || *culture == '\0') {
1788 local_culture = "";
1789 } else {
1790 local_culture = culture;
1793 filename = g_strconcat (name, ".dll", NULL);
1794 len = strlen (filename);
1796 for (path = search_path; *path; path++) {
1797 if (**path == '\0') {
1798 is_private = TRUE;
1799 continue; /* Ignore empty ApplicationBase */
1802 /* See test cases in bug #58992 and bug #57710 */
1803 /* 1st try: [culture]/[name].dll (culture may be empty) */
1804 strcpy (filename + len - 4, ".dll");
1805 if (try_load_from (&result, *path, local_culture, "", filename, refonly, is_private))
1806 break;
1808 /* 2nd try: [culture]/[name].exe (culture may be empty) */
1809 strcpy (filename + len - 4, ".exe");
1810 if (try_load_from (&result, *path, local_culture, "", filename, refonly, is_private))
1811 break;
1813 /* 3rd try: [culture]/[name]/[name].dll (culture may be empty) */
1814 strcpy (filename + len - 4, ".dll");
1815 if (try_load_from (&result, *path, local_culture, name, filename, refonly, is_private))
1816 break;
1818 /* 4th try: [culture]/[name]/[name].exe (culture may be empty) */
1819 strcpy (filename + len - 4, ".exe");
1820 if (try_load_from (&result, *path, local_culture, name, filename, refonly, is_private))
1821 break;
1824 g_free (filename);
1825 return result;
1829 * Try loading the assembly from ApplicationBase and PrivateBinPath
1830 * and then from assemblies_path if any.
1831 * LOCKING: This is called from the assembly loading code, which means the caller
1832 * might hold the loader lock. Thus, this function must not acquire the domain lock.
1834 static MonoAssembly *
1835 mono_domain_assembly_preload (MonoAssemblyName *aname,
1836 gchar **assemblies_path,
1837 gpointer user_data)
1839 MonoDomain *domain = mono_domain_get ();
1840 MonoAssembly *result = NULL;
1841 gboolean refonly = GPOINTER_TO_UINT (user_data);
1843 set_domain_search_path (domain);
1845 if (domain->search_path && domain->search_path [0] != NULL) {
1846 result = real_load (domain->search_path, aname->culture, aname->name, refonly);
1849 if (result == NULL && assemblies_path && assemblies_path [0] != NULL) {
1850 result = real_load (assemblies_path, aname->culture, aname->name, refonly);
1853 return result;
1857 * Check whenever a given assembly was already loaded in the current appdomain.
1859 static MonoAssembly *
1860 mono_domain_assembly_search (MonoAssemblyName *aname,
1861 gpointer user_data)
1863 MonoDomain *domain = mono_domain_get ();
1864 GSList *tmp;
1865 MonoAssembly *ass;
1866 gboolean refonly = GPOINTER_TO_UINT (user_data);
1868 mono_domain_assemblies_lock (domain);
1869 for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) {
1870 ass = tmp->data;
1871 /* Dynamic assemblies can't match here in MS.NET */
1872 if (assembly_is_dynamic (ass) || refonly != ass->ref_only || !mono_assembly_names_equal (aname, &ass->aname))
1873 continue;
1875 mono_domain_assemblies_unlock (domain);
1876 return ass;
1878 mono_domain_assemblies_unlock (domain);
1880 return NULL;
1883 MonoReflectionAssembly *
1884 ves_icall_System_Reflection_Assembly_LoadFrom (MonoString *fname, MonoBoolean refOnly)
1886 MonoDomain *domain = mono_domain_get ();
1887 char *name, *filename;
1888 MonoImageOpenStatus status = MONO_IMAGE_OK;
1889 MonoAssembly *ass;
1891 if (fname == NULL) {
1892 MonoException *exc = mono_get_exception_argument_null ("assemblyFile");
1893 mono_set_pending_exception (exc);
1894 return NULL;
1897 name = filename = mono_string_to_utf8 (fname);
1899 ass = mono_assembly_open_full (filename, &status, refOnly);
1901 if (!ass) {
1902 MonoException *exc;
1904 if (status == MONO_IMAGE_IMAGE_INVALID)
1905 exc = mono_get_exception_bad_image_format2 (NULL, fname);
1906 else
1907 exc = mono_get_exception_file_not_found2 (NULL, fname);
1908 g_free (name);
1909 mono_set_pending_exception (exc);
1910 return NULL;
1913 g_free (name);
1915 return mono_assembly_get_object (domain, ass);
1918 MonoReflectionAssembly *
1919 ves_icall_System_AppDomain_LoadAssemblyRaw (MonoAppDomain *ad,
1920 MonoArray *raw_assembly,
1921 MonoArray *raw_symbol_store, MonoObject *evidence,
1922 MonoBoolean refonly)
1924 MonoAssembly *ass;
1925 MonoReflectionAssembly *refass = NULL;
1926 MonoDomain *domain = ad->data;
1927 MonoImageOpenStatus status;
1928 guint32 raw_assembly_len = mono_array_length (raw_assembly);
1929 MonoImage *image = mono_image_open_from_data_full (mono_array_addr (raw_assembly, gchar, 0), raw_assembly_len, TRUE, NULL, refonly);
1931 if (!image) {
1932 mono_set_pending_exception (mono_get_exception_bad_image_format (""));
1933 return NULL;
1936 if (raw_symbol_store != NULL)
1937 mono_debug_open_image_from_memory (image, mono_array_addr (raw_symbol_store, guint8, 0), mono_array_length (raw_symbol_store));
1939 ass = mono_assembly_load_from_full (image, "", &status, refonly);
1942 if (!ass) {
1943 mono_image_close (image);
1944 mono_set_pending_exception (mono_get_exception_bad_image_format (""));
1945 return NULL;
1948 refass = mono_assembly_get_object (domain, ass);
1949 MONO_OBJECT_SETREF (refass, evidence, evidence);
1950 return refass;
1953 MonoReflectionAssembly *
1954 ves_icall_System_AppDomain_LoadAssembly (MonoAppDomain *ad, MonoString *assRef, MonoObject *evidence, MonoBoolean refOnly)
1956 MonoDomain *domain = ad->data;
1957 MonoImageOpenStatus status = MONO_IMAGE_OK;
1958 MonoAssembly *ass;
1959 MonoAssemblyName aname;
1960 MonoReflectionAssembly *refass = NULL;
1961 gchar *name;
1962 gboolean parsed;
1964 g_assert (assRef != NULL);
1966 name = mono_string_to_utf8 (assRef);
1967 parsed = mono_assembly_name_parse (name, &aname);
1968 g_free (name);
1970 if (!parsed) {
1971 /* This is a parse error... */
1972 if (!refOnly)
1973 refass = mono_try_assembly_resolve (domain, assRef, NULL, refOnly);
1974 return refass;
1977 ass = mono_assembly_load_full_nosearch (&aname, NULL, &status, refOnly);
1978 mono_assembly_name_free (&aname);
1980 if (!ass) {
1981 /* MS.NET doesn't seem to call the assembly resolve handler for refonly assemblies */
1982 if (!refOnly)
1983 refass = mono_try_assembly_resolve (domain, assRef, NULL, refOnly);
1984 else
1985 refass = NULL;
1986 if (!refass) {
1987 return NULL;
1991 if (refass == NULL)
1992 refass = mono_assembly_get_object (domain, ass);
1994 MONO_OBJECT_SETREF (refass, evidence, evidence);
1995 return refass;
1998 void
1999 ves_icall_System_AppDomain_InternalUnload (gint32 domain_id)
2001 MonoDomain * domain = mono_domain_get_by_id (domain_id);
2003 if (NULL == domain) {
2004 MonoException *exc = mono_get_exception_execution_engine ("Failed to unload domain, domain id not found");
2005 mono_set_pending_exception (exc);
2006 return;
2009 if (domain == mono_get_root_domain ()) {
2010 mono_set_pending_exception (mono_get_exception_cannot_unload_appdomain ("The default appdomain can not be unloaded."));
2011 return;
2015 * Unloading seems to cause problems when running NUnit/NAnt, hence
2016 * this workaround.
2018 if (g_getenv ("MONO_NO_UNLOAD"))
2019 return;
2020 #ifdef __native_client__
2021 return;
2022 #endif
2024 mono_domain_unload (domain);
2027 gboolean
2028 ves_icall_System_AppDomain_InternalIsFinalizingForUnload (gint32 domain_id)
2030 MonoDomain *domain = mono_domain_get_by_id (domain_id);
2032 if (!domain)
2033 return TRUE;
2035 return mono_domain_is_unloading (domain);
2038 gint32
2039 ves_icall_System_AppDomain_ExecuteAssembly (MonoAppDomain *ad,
2040 MonoReflectionAssembly *refass, MonoArray *args)
2042 MonoError error;
2043 MonoImage *image;
2044 MonoMethod *method;
2046 g_assert (refass);
2047 image = refass->assembly->image;
2048 g_assert (image);
2050 method = mono_get_method_checked (image, mono_image_get_entry_point (image), NULL, NULL, &error);
2052 if (!method)
2053 g_error ("No entry point method found in %s due to %s", image->name, mono_error_get_message (&error));
2055 if (!args)
2056 args = (MonoArray *) mono_array_new (ad->data, mono_defaults.string_class, 0);
2058 return mono_runtime_exec_main (method, (MonoArray *)args, NULL);
2061 gint32
2062 ves_icall_System_AppDomain_GetIDFromDomain (MonoAppDomain * ad)
2064 return ad->data->domain_id;
2067 MonoAppDomain *
2068 ves_icall_System_AppDomain_InternalSetDomain (MonoAppDomain *ad)
2070 MonoDomain *old_domain = mono_domain_get();
2072 if (!mono_domain_set (ad->data, FALSE)) {
2073 mono_set_pending_exception (mono_get_exception_appdomain_unloaded ());
2074 return NULL;
2077 return old_domain->domain;
2080 MonoAppDomain *
2081 ves_icall_System_AppDomain_InternalSetDomainByID (gint32 domainid)
2083 MonoDomain *current_domain = mono_domain_get ();
2084 MonoDomain *domain = mono_domain_get_by_id (domainid);
2086 if (!domain || !mono_domain_set (domain, FALSE)) {
2087 mono_set_pending_exception (mono_get_exception_appdomain_unloaded ());
2088 return NULL;
2091 return current_domain->domain;
2094 void
2095 ves_icall_System_AppDomain_InternalPushDomainRef (MonoAppDomain *ad)
2097 mono_thread_push_appdomain_ref (ad->data);
2100 void
2101 ves_icall_System_AppDomain_InternalPushDomainRefByID (gint32 domain_id)
2103 MonoDomain *domain = mono_domain_get_by_id (domain_id);
2105 if (!domain) {
2107 * Raise an exception to prevent the managed code from executing a pop
2108 * later.
2110 mono_set_pending_exception (mono_get_exception_appdomain_unloaded ());
2111 return;
2114 mono_thread_push_appdomain_ref (domain);
2117 void
2118 ves_icall_System_AppDomain_InternalPopDomainRef (void)
2120 mono_thread_pop_appdomain_ref ();
2123 MonoAppContext *
2124 ves_icall_System_AppDomain_InternalGetContext ()
2126 return mono_context_get ();
2129 MonoAppContext *
2130 ves_icall_System_AppDomain_InternalGetDefaultContext ()
2132 return mono_domain_get ()->default_context;
2135 MonoAppContext *
2136 ves_icall_System_AppDomain_InternalSetContext (MonoAppContext *mc)
2138 MonoAppContext *old_context = mono_context_get ();
2140 mono_context_set (mc);
2142 return old_context;
2145 MonoString *
2146 ves_icall_System_AppDomain_InternalGetProcessGuid (MonoString* newguid)
2148 MonoDomain* mono_root_domain = mono_get_root_domain ();
2149 mono_domain_lock (mono_root_domain);
2150 if (process_guid_set) {
2151 mono_domain_unlock (mono_root_domain);
2152 return mono_string_new_utf16 (mono_domain_get (), process_guid, sizeof(process_guid)/2);
2154 memcpy (process_guid, mono_string_chars(newguid), sizeof(process_guid));
2155 process_guid_set = TRUE;
2156 mono_domain_unlock (mono_root_domain);
2157 return newguid;
2160 gboolean
2161 mono_domain_is_unloading (MonoDomain *domain)
2163 if (domain->state == MONO_APPDOMAIN_UNLOADING || domain->state == MONO_APPDOMAIN_UNLOADED)
2164 return TRUE;
2165 else
2166 return FALSE;
2169 static void
2170 clear_cached_vtable (MonoVTable *vtable)
2172 MonoClass *klass = vtable->klass;
2173 MonoDomain *domain = vtable->domain;
2174 MonoClassRuntimeInfo *runtime_info;
2175 void *data;
2177 runtime_info = klass->runtime_info;
2178 if (runtime_info && runtime_info->max_domain >= domain->domain_id)
2179 runtime_info->domain_vtables [domain->domain_id] = NULL;
2180 if (klass->has_static_refs && (data = mono_vtable_get_static_field_data (vtable)))
2181 mono_gc_free_fixed (data);
2184 static G_GNUC_UNUSED void
2185 zero_static_data (MonoVTable *vtable)
2187 MonoClass *klass = vtable->klass;
2188 void *data;
2190 if (klass->has_static_refs && (data = mono_vtable_get_static_field_data (vtable)))
2191 mono_gc_bzero_aligned (data, mono_class_data_size (klass));
2194 typedef struct unload_data {
2195 gboolean done;
2196 MonoDomain *domain;
2197 char *failure_reason;
2198 gint32 refcount;
2199 } unload_data;
2201 static void
2202 unload_data_unref (unload_data *data)
2204 gint32 count;
2205 do {
2206 mono_atomic_load_acquire (count, gint32, &data->refcount);
2207 g_assert (count >= 1 && count <= 2);
2208 if (count == 1) {
2209 g_free (data);
2210 return;
2212 } while (InterlockedCompareExchange (&data->refcount, count - 1, count) != count);
2215 static void
2216 deregister_reflection_info_roots_from_list (MonoImage *image)
2218 GSList *list = image->reflection_info_unregister_classes;
2220 while (list) {
2221 MonoClass *class = list->data;
2223 mono_class_free_ref_info (class);
2225 list = list->next;
2228 image->reflection_info_unregister_classes = NULL;
2231 static void
2232 deregister_reflection_info_roots (MonoDomain *domain)
2234 GSList *list;
2236 mono_domain_assemblies_lock (domain);
2237 for (list = domain->domain_assemblies; list; list = list->next) {
2238 MonoAssembly *assembly = list->data;
2239 MonoImage *image = assembly->image;
2240 int i;
2243 * No need to take the image lock here since dynamic images are appdomain bound and
2244 * at this point the mutator is gone. Taking the image lock here would mean
2245 * promoting it from a simple lock to a complex lock, which we better avoid if
2246 * possible.
2248 if (image_is_dynamic (image))
2249 deregister_reflection_info_roots_from_list (image);
2251 for (i = 0; i < image->module_count; ++i) {
2252 MonoImage *module = image->modules [i];
2253 if (module && image_is_dynamic (module))
2254 deregister_reflection_info_roots_from_list (module);
2257 mono_domain_assemblies_unlock (domain);
2260 static guint32 WINAPI
2261 unload_thread_main (void *arg)
2263 unload_data *data = (unload_data*)arg;
2264 MonoDomain *domain = data->domain;
2265 MonoThread *thread;
2266 int i;
2268 /* Have to attach to the runtime so shutdown can wait for this thread */
2269 /* Force it to be attached to avoid racing during shutdown. */
2270 thread = mono_thread_attach_full (mono_get_root_domain (), TRUE);
2273 * FIXME: Abort our parent thread last, so we can return a failure
2274 * indication if aborting times out.
2276 if (!mono_threads_abort_appdomain_threads (domain, -1)) {
2277 data->failure_reason = g_strdup_printf ("Aborting of threads in domain %s timed out.", domain->friendly_name);
2278 goto failure;
2281 if (!mono_thread_pool_remove_domain_jobs (domain, -1)) {
2282 data->failure_reason = g_strdup_printf ("Cleanup of threadpool jobs of domain %s timed out.", domain->friendly_name);
2283 goto failure;
2286 /* Finalize all finalizable objects in the doomed appdomain */
2287 if (!mono_domain_finalize (domain, -1)) {
2288 data->failure_reason = g_strdup_printf ("Finalization of domain %s timed out.", domain->friendly_name);
2289 goto failure;
2292 /* Clear references to our vtables in class->runtime_info.
2293 * We also hold the loader lock because we're going to change
2294 * class->runtime_info.
2297 mono_loader_lock (); //FIXME why do we need the loader lock here?
2298 mono_domain_lock (domain);
2299 #ifdef HAVE_SGEN_GC
2301 * We need to make sure that we don't have any remsets
2302 * pointing into static data of the to-be-freed domain because
2303 * at the next collections they would be invalid. So what we
2304 * do is we first zero all static data and then do a minor
2305 * collection. Because all references in the static data will
2306 * now be null we won't do any unnecessary copies and after
2307 * the collection there won't be any more remsets.
2309 for (i = 0; i < domain->class_vtable_array->len; ++i)
2310 zero_static_data (g_ptr_array_index (domain->class_vtable_array, i));
2311 mono_gc_collect (0);
2312 #endif
2313 for (i = 0; i < domain->class_vtable_array->len; ++i)
2314 clear_cached_vtable (g_ptr_array_index (domain->class_vtable_array, i));
2315 deregister_reflection_info_roots (domain);
2317 mono_assembly_cleanup_domain_bindings (domain->domain_id);
2319 mono_domain_unlock (domain);
2320 mono_loader_unlock ();
2322 mono_threads_clear_cached_culture (domain);
2324 domain->state = MONO_APPDOMAIN_UNLOADED;
2326 /* printf ("UNLOADED %s.\n", domain->friendly_name); */
2328 /* remove from the handle table the items related to this domain */
2329 mono_gchandle_free_domain (domain);
2331 mono_domain_free (domain, FALSE);
2333 mono_gc_collect (mono_gc_max_generation ());
2335 mono_atomic_store_release (&data->done, TRUE);
2336 unload_data_unref (data);
2337 mono_thread_detach (thread);
2338 return 0;
2340 failure:
2341 mono_atomic_store_release (&data->done, TRUE);
2342 unload_data_unref (data);
2343 mono_thread_detach (thread);
2344 return 1;
2348 * mono_domain_unload:
2349 * @domain: The domain to unload
2351 * Unloads an appdomain. Follows the process outlined in the comment
2352 * for mono_domain_try_unload.
2354 void
2355 mono_domain_unload (MonoDomain *domain)
2357 MonoObject *exc = NULL;
2358 mono_domain_try_unload (domain, &exc);
2359 if (exc)
2360 mono_raise_exception ((MonoException*)exc);
2363 static guint32
2364 guarded_wait (HANDLE handle, guint32 timeout, gboolean alertable)
2366 guint32 result;
2368 MONO_PREPARE_BLOCKING
2369 result = WaitForSingleObjectEx (handle, timeout, alertable);
2370 MONO_FINISH_BLOCKING
2372 return result;
2376 * mono_domain_unload:
2377 * @domain: The domain to unload
2378 * @exc: Exception information
2380 * Unloads an appdomain. Follows the process outlined in:
2381 * http://blogs.gotdotnet.com/cbrumme
2383 * If doing things the 'right' way is too hard or complex, we do it the
2384 * 'simple' way, which means do everything needed to avoid crashes and
2385 * memory leaks, but not much else.
2387 * It is required to pass a valid reference to the exc argument, upon return
2388 * from this function *exc will be set to the exception thrown, if any.
2390 * If this method is not called from an icall (embedded scenario for instance),
2391 * it must not be called with any managed frames on the stack, since the unload
2392 * process could end up trying to abort the current thread.
2394 void
2395 mono_domain_try_unload (MonoDomain *domain, MonoObject **exc)
2397 HANDLE thread_handle;
2398 MonoAppDomainState prev_state;
2399 MonoMethod *method;
2400 unload_data *thread_data;
2401 MonoNativeThreadId tid;
2402 MonoDomain *caller_domain = mono_domain_get ();
2403 char *name;
2405 /* printf ("UNLOAD STARTING FOR %s (%p) IN THREAD 0x%x.\n", domain->friendly_name, domain, GetCurrentThreadId ()); */
2407 /* Atomically change our state to UNLOADING */
2408 prev_state = InterlockedCompareExchange ((gint32*)&domain->state,
2409 MONO_APPDOMAIN_UNLOADING_START,
2410 MONO_APPDOMAIN_CREATED);
2411 if (prev_state != MONO_APPDOMAIN_CREATED) {
2412 switch (prev_state) {
2413 case MONO_APPDOMAIN_UNLOADING_START:
2414 case MONO_APPDOMAIN_UNLOADING:
2415 *exc = (MonoObject *) mono_get_exception_cannot_unload_appdomain ("Appdomain is already being unloaded.");
2416 return;
2417 case MONO_APPDOMAIN_UNLOADED:
2418 *exc = (MonoObject *) mono_get_exception_cannot_unload_appdomain ("Appdomain is already unloaded.");
2419 return;
2420 default:
2421 g_warning ("Invalid appdomain state %d", prev_state);
2422 g_assert_not_reached ();
2426 mono_domain_set (domain, FALSE);
2427 /* Notify OnDomainUnload listeners */
2428 method = mono_class_get_method_from_name (domain->domain->mbr.obj.vtable->klass, "DoDomainUnload", -1);
2429 g_assert (method);
2431 mono_runtime_invoke (method, domain->domain, NULL, exc);
2432 if (*exc) {
2433 /* Roll back the state change */
2434 domain->state = MONO_APPDOMAIN_CREATED;
2435 mono_domain_set (caller_domain, FALSE);
2436 return;
2438 mono_domain_set (caller_domain, FALSE);
2440 thread_data = g_new0 (unload_data, 1);
2441 thread_data->domain = domain;
2442 thread_data->failure_reason = NULL;
2443 thread_data->done = FALSE;
2444 thread_data->refcount = 2; /*Must be 2: unload thread + initiator */
2446 /*The managed callback finished successfully, now we start tearing down the appdomain*/
2447 domain->state = MONO_APPDOMAIN_UNLOADING;
2449 * First we create a separate thread for unloading, since
2450 * we might have to abort some threads, including the current one.
2452 thread_handle = mono_threads_create_thread ((LPTHREAD_START_ROUTINE)unload_thread_main, thread_data, 0, CREATE_SUSPENDED, &tid);
2453 if (thread_handle == NULL)
2454 return;
2455 name = g_strdup_printf ("Unload thread for domain %x", domain);
2456 mono_thread_info_set_name (tid, name);
2457 mono_thread_info_resume (tid);
2458 g_free (name);
2460 /* Wait for the thread */
2461 while (!thread_data->done && guarded_wait (thread_handle, INFINITE, TRUE) == WAIT_IO_COMPLETION) {
2462 if (mono_thread_internal_has_appdomain_ref (mono_thread_internal_current (), domain) && (mono_thread_interruption_requested ())) {
2463 /* The unload thread tries to abort us */
2464 /* The icall wrapper will execute the abort */
2465 CloseHandle (thread_handle);
2466 unload_data_unref (thread_data);
2467 return;
2470 CloseHandle (thread_handle);
2472 if (thread_data->failure_reason) {
2473 /* Roll back the state change */
2474 domain->state = MONO_APPDOMAIN_CREATED;
2476 g_warning ("%s", thread_data->failure_reason);
2478 *exc = (MonoObject *) mono_get_exception_cannot_unload_appdomain (thread_data->failure_reason);
2480 g_free (thread_data->failure_reason);
2481 thread_data->failure_reason = NULL;
2484 unload_data_unref (thread_data);