2006-08-16 Gonzalo Paniagua Javier <gonzalo@ximian.com>
[mono.git] / mono / metadata / appdomain.c
1 /*
2  * appdomain.c: AppDomain functions
3  *
4  * Authors:
5  *      Dietmar Maurer (dietmar@ximian.com)
6  *      Patrik Torstensson
7  *      Gonzalo Paniagua Javier (gonzalo@ximian.com)
8  *
9  * (c) 2001-2003 Ximian, Inc. (http://www.ximian.com)
10  */
11
12 #include <config.h>
13 #include <glib.h>
14 #include <string.h>
15
16 #include <mono/os/gc_wrapper.h>
17
18 #include <mono/metadata/object.h>
19 #include <mono/metadata/domain-internals.h>
20 #include "mono/metadata/metadata-internals.h"
21 #include <mono/metadata/assembly.h>
22 #include <mono/metadata/exception.h>
23 #include <mono/metadata/threads.h>
24 #include <mono/metadata/socket-io.h>
25 #include <mono/metadata/tabledefs.h>
26 #include <mono/metadata/gc-internal.h>
27 #include <mono/metadata/mono-gc.h>
28 #include <mono/metadata/marshal.h>
29 #include <mono/metadata/monitor.h>
30 #include <mono/metadata/threadpool.h>
31 #include <mono/utils/mono-uri.h>
32 #include <mono/utils/mono-logger.h>
33
34 #define MONO_CORLIB_VERSION 54
35
36 CRITICAL_SECTION mono_delegate_section;
37
38 static gunichar2 process_guid [36];
39 static gboolean process_guid_set = FALSE;
40
41 static gboolean shutting_down = FALSE;
42
43 static MonoAssembly *
44 mono_domain_assembly_preload (MonoAssemblyName *aname,
45                               gchar **assemblies_path,
46                               gpointer user_data);
47
48 static MonoAssembly *
49 mono_domain_assembly_search (MonoAssemblyName *aname,
50                                                          gpointer user_data);
51
52 static MonoAssembly *
53 mono_domain_assembly_postload_search (MonoAssemblyName *aname,
54                                                                           gpointer user_data);
55
56 static void
57 mono_domain_fire_assembly_load (MonoAssembly *assembly, gpointer user_data);
58
59 static void
60 add_assemblies_to_domain (MonoDomain *domain, MonoAssembly *ass, GHashTable *hash);
61
62 static void
63 mono_domain_unload (MonoDomain *domain);
64
65 /**
66  * mono_runtime_init:
67  * @domain: domain returned by mono_init ()
68  *
69  * Initialize the core AppDomain: this function will run also some
70  * IL initialization code, so it needs the execution engine to be fully 
71  * operational.
72  *
73  * AppDomain.SetupInformation is set up in mono_runtime_exec_main, where
74  * we know the entry_assembly.
75  *
76  */
77 void
78 mono_runtime_init (MonoDomain *domain, MonoThreadStartCB start_cb,
79                    MonoThreadAttachCB attach_cb)
80 {
81         MonoAppDomainSetup *setup;
82         MonoAppDomain *ad;
83         MonoClass *class;
84         MonoString *arg;
85         
86         MONO_GC_PRE_INIT ();
87         mono_monitor_init ();
88         mono_thread_pool_init ();
89         mono_marshal_init ();
90
91         mono_install_assembly_preload_hook (mono_domain_assembly_preload, GUINT_TO_POINTER (FALSE));
92         mono_install_assembly_refonly_preload_hook (mono_domain_assembly_preload, GUINT_TO_POINTER (TRUE));
93         mono_install_assembly_search_hook (mono_domain_assembly_search, GUINT_TO_POINTER (FALSE));
94         mono_install_assembly_refonly_search_hook (mono_domain_assembly_search, GUINT_TO_POINTER (TRUE));
95         mono_install_assembly_postload_search_hook (mono_domain_assembly_postload_search, GUINT_TO_POINTER (FALSE));
96         mono_install_assembly_postload_refonly_search_hook (mono_domain_assembly_postload_search, GUINT_TO_POINTER (TRUE));
97         mono_install_assembly_load_hook (mono_domain_fire_assembly_load, NULL);
98         mono_install_lookup_dynamic_token (mono_reflection_lookup_dynamic_token);
99
100         mono_thread_init (start_cb, attach_cb);
101
102         class = mono_class_from_name (mono_defaults.corlib, "System", "AppDomainSetup");
103         setup = (MonoAppDomainSetup *) mono_object_new (domain, class);
104
105         class = mono_class_from_name (mono_defaults.corlib, "System", "AppDomain");
106         ad = (MonoAppDomain *) mono_object_new (domain, class);
107         ad->data = domain;
108         domain->domain = ad;
109         domain->setup = setup;
110
111         InitializeCriticalSection (&mono_delegate_section);
112
113         mono_thread_attach (domain);
114         mono_context_init (domain);
115         mono_context_set (domain->default_context);
116
117         mono_type_initialization_init ();
118
119         
120         /*
121          * Create an instance early since we can't do it when there is no memory.
122          */
123         arg = mono_string_new (domain, "Out of memory");
124         domain->out_of_memory_ex = mono_exception_from_name_two_strings (mono_defaults.corlib, "System", "OutOfMemoryException", arg, NULL);
125         
126         /* 
127          * These two are needed because the signal handlers might be executing on
128          * an alternate stack, and Boehm GC can't handle that.
129          */
130         arg = mono_string_new (domain, "A null value was found where an object instance was required");
131         domain->null_reference_ex = mono_exception_from_name_two_strings (mono_defaults.corlib, "System", "NullReferenceException", arg, NULL);
132         arg = mono_string_new (domain, "The requested operation caused a stack overflow.");
133         domain->stack_overflow_ex = mono_exception_from_name_two_strings (mono_defaults.corlib, "System", "StackOverflowException", arg, NULL);
134         
135         /* GC init has to happen after thread init */
136         mono_gc_init ();
137
138         mono_network_init ();
139
140         /* mscorlib is loaded before we install the load hook */
141         mono_domain_fire_assembly_load (mono_defaults.corlib->assembly, NULL);
142
143         return;
144 }
145
146 static int
147 mono_get_corlib_version (void)
148 {
149         MonoClass *klass;
150         MonoClassField *field;
151         MonoObject *value;
152
153         klass = mono_class_from_name (mono_defaults.corlib, "System", "Environment");
154         mono_class_init (klass);
155         field = mono_class_get_field_from_name (klass, "mono_corlib_version");
156         if (!field)
157                 return -1;
158         if (! (field->type->attrs & FIELD_ATTRIBUTE_STATIC))
159                 return -1;
160         value = mono_field_get_value_object (mono_domain_get (), field, NULL);
161         return *(gint32*)((gchar*)value + sizeof (MonoObject));
162 }
163
164 const char*
165 mono_check_corlib_version (void)
166 {
167         int version = mono_get_corlib_version ();
168         if (version != MONO_CORLIB_VERSION)
169                 return g_strdup_printf ("expected corlib version %d, found %d.", MONO_CORLIB_VERSION, version);
170         else
171                 return NULL;
172 }
173
174 void
175 mono_context_init (MonoDomain *domain)
176 {
177         MonoClass *class;
178         MonoAppContext *context;
179
180         class = mono_class_from_name (mono_defaults.corlib, "System.Runtime.Remoting.Contexts", "Context");
181         context = (MonoAppContext *) mono_object_new (domain, class);
182         context->domain_id = domain->domain_id;
183         context->context_id = 0;
184         domain->default_context = context;
185 }
186
187 /**
188  * mono_runtime_cleanup:
189  * @domain: unused.
190  *
191  * Internal routine.
192  *
193  * This must not be called while there are still running threads executing
194  * managed code.
195  */
196 void
197 mono_runtime_cleanup (MonoDomain *domain)
198 {
199         shutting_down = TRUE;
200
201         /* This ends up calling any pending pending (for at most 2 seconds) */
202         mono_gc_cleanup ();
203
204         mono_thread_cleanup ();
205
206         mono_network_cleanup ();
207
208         mono_marshal_cleanup ();
209
210         mono_type_initialization_cleanup ();
211
212         mono_monitor_cleanup ();
213 }
214
215 static MonoDomainFunc quit_function = NULL;
216
217 void
218 mono_install_runtime_cleanup (MonoDomainFunc func)
219 {
220         quit_function = func;
221 }
222
223 void
224 mono_runtime_quit ()
225 {
226         if (quit_function != NULL)
227                 quit_function (mono_get_root_domain (), NULL);
228 }
229
230 /** 
231  * mono_runtime_set_shutting_down:
232  *
233  * Invoked by System.Environment.Exit to flag that the runtime
234  * is shutting down.
235  */
236 void
237 mono_runtime_set_shutting_down (void)
238 {
239         shutting_down = TRUE;
240 }
241
242 /**
243  * mono_runtime_is_shutting_down:
244  *
245  * Returns whether the runtime has been flagged for shutdown.
246  *
247  * This is consumed by the P:System.Environment.HasShutdownStarted
248  * property.
249  *
250  */
251 gboolean
252 mono_runtime_is_shutting_down (void)
253 {
254         return shutting_down;
255 }
256
257 /**
258  * mono_domain_has_type_resolve:
259  * @domain: application domains being looked up
260  *
261  * Returns true if the AppDomain.TypeResolve field has been
262  * set.
263  */
264 gboolean
265 mono_domain_has_type_resolve (MonoDomain *domain)
266 {
267         static MonoClassField *field = NULL;
268         MonoObject *o;
269
270         if (field == NULL) {
271                 field = mono_class_get_field_from_name (mono_defaults.appdomain_class, "TypeResolve");
272                 g_assert (field);
273         }
274
275         mono_field_get_value ((MonoObject*)(domain->domain), field, &o);
276         return o != NULL;
277 }
278
279 /**
280  * mono_domain_try_type_resolve:
281  * @domain: application domainwhere the name where the type is going to be resolved
282  * @name: the name of the type to resolve or NULL.
283  * @tb: A System.Reflection.Emit.TypeBuilder, used if name is NULL.
284  *
285  * This routine invokes the internal System.AppDomain.DoTypeResolve and returns
286  * the assembly that matches name.
287  *
288  * If @name is null, the value of ((TypeBuilder)tb).FullName is used instead
289  *
290  * Returns: A MonoReflectionAssembly or NULL if not found
291  */
292 MonoReflectionAssembly *
293 mono_domain_try_type_resolve (MonoDomain *domain, char *name, MonoObject *tb)
294 {
295         MonoClass *klass;
296         void *params [1];
297         static MonoMethod *method = NULL;
298
299         g_assert (domain != NULL && ((name != NULL) || (tb != NULL)));
300
301         if (method == NULL) {
302                 klass = domain->domain->mbr.obj.vtable->klass;
303                 g_assert (klass);
304
305                 method = mono_class_get_method_from_name (klass, "DoTypeResolve", -1);
306                 if (method == NULL) {
307                         g_warning ("Method AppDomain.DoTypeResolve not found.\n");
308                         return NULL;
309                 }
310         }
311
312         if (name)
313                 *params = (MonoObject*)mono_string_new (mono_domain_get (), name);
314         else
315                 *params = tb;
316         return (MonoReflectionAssembly *) mono_runtime_invoke (method, domain->domain, params, NULL);
317 }
318
319 /**
320  * mono_domain_owns_vtable_slot:
321  *
322  *  Returns whenever VTABLE_SLOT is inside a vtable which belongs to DOMAIN.
323  */
324 gboolean
325 mono_domain_owns_vtable_slot (MonoDomain *domain, gpointer vtable_slot)
326 {
327         gboolean res;
328
329         mono_domain_lock (domain);
330         res = mono_mempool_contains_addr (domain->mp, vtable_slot);
331         mono_domain_unlock (domain);
332         return res;
333 }
334
335 /**
336  * mono_domain_set:
337  * @domain: domain
338  * @force: force setting.
339  *
340  * Set the current appdomain to @domain. If @force is set, set it even
341  * if it is being unloaded.
342  *
343  * Returns:
344  *   TRUE on success;
345  *   FALSE if the domain is unloaded
346  */
347 gboolean
348 mono_domain_set (MonoDomain *domain, gboolean force)
349 {
350         if (!force && domain->state == MONO_APPDOMAIN_UNLOADED)
351                 return FALSE;
352
353         mono_domain_set_internal (domain);
354
355         return TRUE;
356 }
357
358 MonoObject *
359 ves_icall_System_AppDomain_GetData (MonoAppDomain *ad, MonoString *name)
360 {
361         MonoDomain *add;
362         MonoObject *o;
363         char *str;
364
365         MONO_ARCH_SAVE_REGS;
366
367         g_assert (ad != NULL);
368         add = ad->data;
369         g_assert (add != NULL);
370
371         if (name == NULL)
372                 mono_raise_exception (mono_get_exception_argument_null ("name"));
373
374         str = mono_string_to_utf8 (name);
375
376         mono_domain_lock (add);
377
378         if (!strcmp (str, "APPBASE"))
379                 o = (MonoObject *)add->setup->application_base;
380         else if (!strcmp (str, "APP_CONFIG_FILE"))
381                 o = (MonoObject *)add->setup->configuration_file;
382         else if (!strcmp (str, "DYNAMIC_BASE"))
383                 o = (MonoObject *)add->setup->dynamic_base;
384         else if (!strcmp (str, "APP_NAME"))
385                 o = (MonoObject *)add->setup->application_name;
386         else if (!strcmp (str, "CACHE_BASE"))
387                 o = (MonoObject *)add->setup->cache_path;
388         else if (!strcmp (str, "PRIVATE_BINPATH"))
389                 o = (MonoObject *)add->setup->private_bin_path;
390         else if (!strcmp (str, "BINPATH_PROBE_ONLY"))
391                 o = (MonoObject *)add->setup->private_bin_path_probe;
392         else if (!strcmp (str, "SHADOW_COPY_DIRS"))
393                 o = (MonoObject *)add->setup->shadow_copy_directories;
394         else if (!strcmp (str, "FORCE_CACHE_INSTALL"))
395                 o = (MonoObject *)add->setup->shadow_copy_files;
396         else 
397                 o = mono_g_hash_table_lookup (add->env, name);
398
399         mono_domain_unlock (add);
400         g_free (str);
401
402         if (!o)
403                 return NULL;
404
405         return o;
406 }
407
408 void
409 ves_icall_System_AppDomain_SetData (MonoAppDomain *ad, MonoString *name, MonoObject *data)
410 {
411         MonoDomain *add;
412
413         MONO_ARCH_SAVE_REGS;
414
415         g_assert (ad != NULL);
416         add = ad->data;
417         g_assert (add != NULL);
418
419         if (name == NULL)
420                 mono_raise_exception (mono_get_exception_argument_null ("name"));
421
422         mono_domain_lock (add);
423
424         mono_g_hash_table_insert (add->env, name, data);
425
426         mono_domain_unlock (add);
427 }
428
429 MonoAppDomainSetup *
430 ves_icall_System_AppDomain_getSetup (MonoAppDomain *ad)
431 {
432         MONO_ARCH_SAVE_REGS;
433
434         g_assert (ad != NULL);
435         g_assert (ad->data != NULL);
436
437         return ad->data->setup;
438 }
439
440 MonoString *
441 ves_icall_System_AppDomain_getFriendlyName (MonoAppDomain *ad)
442 {
443         MONO_ARCH_SAVE_REGS;
444
445         g_assert (ad != NULL);
446         g_assert (ad->data != NULL);
447
448         return mono_string_new (ad->data, ad->data->friendly_name);
449 }
450
451 MonoAppDomain *
452 ves_icall_System_AppDomain_getCurDomain ()
453 {
454         MonoDomain *add = mono_domain_get ();
455
456         MONO_ARCH_SAVE_REGS;
457
458         return add->domain;
459 }
460
461 MonoAppDomain *
462 ves_icall_System_AppDomain_getRootDomain ()
463 {
464         MonoDomain *root = mono_get_root_domain ();
465
466         MONO_ARCH_SAVE_REGS;
467
468         return root->domain;
469 }
470
471 MonoAppDomain *
472 ves_icall_System_AppDomain_createDomain (MonoString *friendly_name, MonoAppDomainSetup *setup)
473 {
474         MonoDomain *domain = mono_domain_get ();
475         MonoClass *adclass;
476         MonoAppDomain *ad;
477         MonoDomain *data;
478         GSList *tmp;
479         
480         MONO_ARCH_SAVE_REGS;
481
482         adclass = mono_class_from_name (mono_defaults.corlib, "System", "AppDomain");
483
484         /* FIXME: pin all those objects */
485         data = mono_domain_create();
486
487         ad = (MonoAppDomain *) mono_object_new (data, adclass);
488         ad->data = data;
489         data->domain = ad;
490         data->setup = setup;
491         data->friendly_name = mono_string_to_utf8 (friendly_name);
492         data->out_of_memory_ex = mono_exception_from_name_domain (data, mono_defaults.corlib, "System", "OutOfMemoryException");
493
494         if (!setup->application_base) {
495                 /* Inherit from the root domain since MS.NET does this */
496                 MonoDomain *root = mono_get_root_domain ();
497                 if (root->setup->application_base)
498                         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)));
499         }
500
501         mono_context_init (data);
502
503         add_assemblies_to_domain (data, mono_defaults.corlib->assembly, NULL);
504
505         return ad;
506 }
507
508 MonoArray *
509 ves_icall_System_AppDomain_GetAssemblies (MonoAppDomain *ad, MonoBoolean refonly)
510 {
511         MonoDomain *domain = ad->data; 
512         MonoAssembly* ass;
513         static MonoClass *System_Reflection_Assembly;
514         MonoArray *res;
515         GSList *tmp;
516         int i, count;
517         
518         MONO_ARCH_SAVE_REGS;
519
520         if (!System_Reflection_Assembly)
521                 System_Reflection_Assembly = mono_class_from_name (
522                         mono_defaults.corlib, "System.Reflection", "Assembly");
523
524         count = 0;
525         /* Need to skip internal assembly builders created by remoting */
526         mono_domain_assemblies_lock (domain);
527         for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) {
528                 ass = tmp->data;
529                 if (refonly && !ass->ref_only)
530                         continue;
531                 if (!ass->corlib_internal)
532                         count++;
533         }
534         res = mono_array_new (domain, System_Reflection_Assembly, count);
535         i = 0;
536         for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) {
537                 ass = tmp->data;
538                 if (refonly && !ass->ref_only)
539                         continue;
540                 if (ass->corlib_internal)
541                         continue;
542                 mono_array_setref (res, i, mono_assembly_get_object (domain, ass));
543                 ++i;
544         }
545         mono_domain_assemblies_unlock (domain);
546
547         return res;
548 }
549
550 static MonoReflectionAssembly *
551 try_assembly_resolve (MonoDomain *domain, MonoString *fname, gboolean refonly)
552 {
553         MonoClass *klass;
554         MonoMethod *method;
555         MonoBoolean isrefonly;
556         gpointer params [2];
557
558         g_assert (domain != NULL && fname != NULL);
559
560         klass = domain->domain->mbr.obj.vtable->klass;
561         g_assert (klass);
562         
563         method = mono_class_get_method_from_name (klass, "DoAssemblyResolve", -1);
564         if (method == NULL) {
565                 g_warning ("Method AppDomain.DoAssemblyResolve not found.\n");
566                 return NULL;
567         }
568
569         isrefonly = refonly ? 1 : 0;
570         params [0] = fname;
571         params [1] = &isrefonly;
572         return (MonoReflectionAssembly *) mono_runtime_invoke (method, domain->domain, params, NULL);
573 }
574
575 static MonoAssembly *
576 mono_domain_assembly_postload_search (MonoAssemblyName *aname,
577                                                                           gpointer user_data)
578 {
579         gboolean refonly = GPOINTER_TO_UINT (user_data);
580         MonoReflectionAssembly *assembly;
581         MonoDomain *domain = mono_domain_get ();
582         char *aname_str;
583
584         aname_str = mono_stringify_assembly_name (aname);
585
586         /* FIXME: We invoke managed code here, so there is a potential for deadlocks */
587         assembly = try_assembly_resolve (domain, mono_string_new (domain, aname_str), refonly);
588
589         g_free (aname_str);
590
591         if (assembly)
592                 return assembly->assembly;
593         else
594                 return NULL;
595 }
596         
597 /*
598  * LOCKING: assumes assemblies_lock in the domain is already locked.
599  */
600 static void
601 add_assemblies_to_domain (MonoDomain *domain, MonoAssembly *ass, GHashTable *ht)
602 {
603         gint i;
604         GSList *tmp;
605         gboolean destroy_ht = FALSE;
606
607         if (!ass->aname.name)
608                 return;
609
610         if (!ht) {
611                 ht = g_hash_table_new (mono_aligned_addr_hash, NULL);
612                 destroy_ht = TRUE;
613         }
614
615         /* FIXME: handle lazy loaded assemblies */
616         for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) {
617                 g_hash_table_insert (ht, tmp->data, tmp->data);
618         }
619         if (!g_hash_table_lookup (ht, ass)) {
620                 mono_assembly_addref (ass);
621                 g_hash_table_insert (ht, ass, ass);
622                 domain->domain_assemblies = g_slist_prepend (domain->domain_assemblies, ass);
623                 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Assembly %s %p added to domain %s, ref_count=%d\n", ass->aname.name, ass, domain->friendly_name, ass->ref_count);
624         }
625
626         if (ass->image->references) {
627                 for (i = 0; ass->image->references [i] != NULL; i++) {
628                         if (!g_hash_table_lookup (ht, ass->image->references [i])) {
629                                 add_assemblies_to_domain (domain, ass->image->references [i], ht);
630                         }
631                 }
632         }
633         if (destroy_ht)
634                 g_hash_table_destroy (ht);
635 }
636
637 static void
638 mono_domain_fire_assembly_load (MonoAssembly *assembly, gpointer user_data)
639 {
640         static MonoClassField *assembly_load_field;
641         static MonoMethod *assembly_load_method;
642         MonoDomain *domain = mono_domain_get ();
643         MonoReflectionAssembly *ref_assembly;
644         MonoClass *klass;
645         gpointer load_value;
646         void *params [1];
647
648         if (!domain->domain)
649                 /* This can happen during startup */
650                 return;
651
652         klass = domain->domain->mbr.obj.vtable->klass;
653
654         mono_domain_assemblies_lock (domain);
655         add_assemblies_to_domain (domain, assembly, NULL);
656         mono_domain_assemblies_unlock (domain);
657
658         if (assembly_load_field == NULL) {
659                 assembly_load_field = mono_class_get_field_from_name (klass, "AssemblyLoad");
660                 g_assert (assembly_load_field);
661         }
662
663         mono_field_get_value ((MonoObject*) domain->domain, assembly_load_field, &load_value);
664         if (load_value == NULL) {
665                 /* No events waiting to be triggered */
666                 return;
667         }
668
669         ref_assembly = mono_assembly_get_object (domain, assembly);
670         g_assert (ref_assembly);
671
672         if (assembly_load_method == NULL) {
673                 assembly_load_method = mono_class_get_method_from_name (klass, "DoAssemblyLoad", -1);
674                 g_assert (assembly_load_method);
675         }
676
677         *params = ref_assembly;
678         mono_runtime_invoke (assembly_load_method, domain->domain, params, NULL);
679 }
680
681 static gchar *
682 reduce_path (const gchar *dirname)
683 {
684         gchar **parts;
685         gchar *part;
686         GList *list, *tmp;
687         GString *result;
688         gchar *res;
689         gint i;
690
691         parts = g_strsplit (dirname, G_DIR_SEPARATOR_S, 0);
692         list = NULL;
693         for (i = 0; (part = parts [i]) != NULL; i++) {
694                 if (!strcmp (part, "."))
695                         continue;
696
697                 if (!strcmp (part, "..")) {
698                         if (list && list->next) /* Don't remove root */
699                                 list = g_list_delete_link (list, list);
700                 } else {
701                         list = g_list_prepend (list, part);
702                 }
703         }
704
705         result = g_string_new ("");
706         list = g_list_reverse (list);
707
708         for (tmp = list; tmp; tmp = tmp->next) {
709                 gchar *data = (gchar *) tmp->data;
710
711                 if (data && *data) {
712 #ifdef PLATFORM_WIN32
713                         if (result->len == 0)
714                                 g_string_append_printf (result, "%s\\", data);
715                         else if (result->str [result->len - 1] == '\\')
716                                 g_string_append_printf (result, "%s", data);
717                         else
718 #endif
719                                 g_string_append_printf (result, "%c%s",
720                                                         G_DIR_SEPARATOR, data);
721                 }
722         }
723         
724         res = result->str;
725         g_string_free (result, FALSE);
726         g_list_free (list);
727         g_strfreev (parts);
728         return res;
729 }
730
731 static void
732 set_domain_search_path (MonoDomain *domain)
733 {
734         MonoAppDomainSetup *setup;
735         gchar **tmp;
736         gchar *utf8;
737         gint i;
738         gint npaths = 0;
739         gchar **pvt_split = NULL;
740         GError *error = NULL;
741         gint appbaselen = -1;
742
743         if ((domain->search_path != NULL) && !domain->setup->path_changed)
744                 return;
745         if (!domain->setup)
746                 return;
747
748         setup = domain->setup;
749         if (!setup->application_base)
750                 return; /* Must set application base to get private path working */
751
752         npaths++;
753         if (setup->private_bin_path) {
754                 utf8 = mono_string_to_utf8 (setup->private_bin_path);
755                 pvt_split = g_strsplit (utf8, G_SEARCHPATH_SEPARATOR_S, 1000);
756                 g_free (utf8);
757                 for (tmp = pvt_split; *tmp; tmp++, npaths++);
758         }
759
760         if (!npaths) {
761                 if (pvt_split)
762                         g_strfreev (pvt_split);
763                 /*
764                  * Don't do this because the first time is called, the domain
765                  * setup is not finished.
766                  *
767                  * domain->search_path = g_malloc (sizeof (char *));
768                  * domain->search_path [0] = NULL;
769                 */
770                 return;
771         }
772
773         if (domain->search_path)
774                 g_strfreev (domain->search_path);
775
776         domain->search_path = tmp = g_malloc ((npaths + 1) * sizeof (gchar *));
777         tmp [npaths] = NULL;
778
779         *tmp = mono_string_to_utf8 (setup->application_base);
780
781         /* FIXME: is this needed? */
782         if (strncmp (*tmp, "file://", 7) == 0) {
783                 gchar *file = *tmp;
784                 gchar *uri = *tmp;
785                 gchar *tmpuri;
786
787                 if (uri [7] != '/')
788                         uri = g_strdup_printf ("file:///%s", uri + 7);
789
790                 tmpuri = uri;
791                 uri = mono_escape_uri_string (tmpuri);
792                 *tmp = g_filename_from_uri (uri, NULL, &error);
793                 g_free (uri);
794
795                 if (tmpuri != file)
796                         g_free (tmpuri);
797
798                 if (error != NULL) {
799                         g_warning ("%s\n", error->message);
800                         g_error_free (error);
801                         *tmp = file;
802                 } else {
803                         g_free (file);
804                 }
805         }
806
807         for (i = 1; pvt_split && i < npaths; i++) {
808                 if (g_path_is_absolute (pvt_split [i - 1])) {
809                         tmp [i] = g_strdup (pvt_split [i - 1]);
810                 } else {
811                         tmp [i] = g_build_filename (tmp [0], pvt_split [i - 1], NULL);
812                 }
813
814                 if (strchr (tmp [i], '.')) {
815                         gchar *reduced;
816                         gchar *freeme;
817
818                         reduced = reduce_path (tmp [i]);
819                         if (appbaselen == -1)
820                                 appbaselen = strlen (tmp [0]);
821
822                         if (strncmp (tmp [0], reduced, appbaselen)) {
823                                 g_free (reduced);
824                                 g_free (tmp [i]);
825                                 tmp [i] = g_strdup ("");
826                                 continue;
827                         }
828
829                         freeme = tmp [i];
830                         tmp [i] = reduced;
831                         g_free (freeme);
832                 }
833         }
834         
835         if (setup->private_bin_path_probe != NULL) {
836                 g_free (tmp [0]);
837                 tmp [0] = g_strdup ("");
838         }
839                 
840         domain->setup->path_changed = FALSE;
841
842         g_strfreev (pvt_split);
843 }
844
845 static gboolean
846 try_load_from (MonoAssembly **assembly, const gchar *path1, const gchar *path2,
847                                         const gchar *path3, const gchar *path4, gboolean refonly)
848 {
849         gchar *fullpath;
850
851         *assembly = NULL;
852         fullpath = g_build_filename (path1, path2, path3, path4, NULL);
853         if (g_file_test (fullpath, G_FILE_TEST_IS_REGULAR))
854                 *assembly = mono_assembly_open_full (fullpath, NULL, refonly);
855
856         g_free (fullpath);
857         return (*assembly != NULL);
858 }
859
860 static MonoAssembly *
861 real_load (gchar **search_path, const gchar *culture, const gchar *name, gboolean refonly)
862 {
863         MonoAssembly *result = NULL;
864         gchar **path;
865         gchar *filename;
866         const gchar *local_culture;
867         gint len;
868
869         if (!culture || *culture == '\0') {
870                 local_culture = "";
871         } else {
872                 local_culture = culture;
873         }
874
875         filename =  g_strconcat (name, ".dll", NULL);
876         len = strlen (filename);
877
878         for (path = search_path; *path; path++) {
879                 if (**path == '\0')
880                         continue; /* Ignore empty ApplicationBase */
881
882                 /* See test cases in bug #58992 and bug #57710 */
883                 /* 1st try: [culture]/[name].dll (culture may be empty) */
884                 strcpy (filename + len - 4, ".dll");
885                 if (try_load_from (&result, *path, local_culture, "", filename, refonly))
886                         break;
887
888                 /* 2nd try: [culture]/[name].exe (culture may be empty) */
889                 strcpy (filename + len - 4, ".exe");
890                 if (try_load_from (&result, *path, local_culture, "", filename, refonly))
891                         break;
892
893                 /* 3rd try: [culture]/[name]/[name].dll (culture may be empty) */
894                 strcpy (filename + len - 4, ".dll");
895                 if (try_load_from (&result, *path, local_culture, name, filename, refonly))
896                         break;
897
898                 /* 4th try: [culture]/[name]/[name].exe (culture may be empty) */
899                 strcpy (filename + len - 4, ".exe");
900                 if (try_load_from (&result, *path, local_culture, name, filename, refonly))
901                         break;
902         }
903
904         g_free (filename);
905         return result;
906 }
907
908 /*
909  * Try loading the assembly from ApplicationBase and PrivateBinPath 
910  * and then from assemblies_path if any.
911  */
912 static MonoAssembly *
913 mono_domain_assembly_preload (MonoAssemblyName *aname,
914                               gchar **assemblies_path,
915                               gpointer user_data)
916 {
917         MonoDomain *domain = mono_domain_get ();
918         MonoAssembly *result = NULL;
919         gboolean refonly = GPOINTER_TO_UINT (user_data);
920
921         set_domain_search_path (domain);
922
923         if (domain->search_path && domain->search_path [0] != NULL) {
924                 result = real_load (domain->search_path, aname->culture, aname->name, refonly);
925         }
926
927         if (result == NULL && assemblies_path && assemblies_path [0] != NULL) {
928                 result = real_load (assemblies_path, aname->culture, aname->name, refonly);
929         }
930
931         return result;
932 }
933
934 /*
935  * Check whenever a given assembly was already loaded in the current appdomain.
936  */
937 static MonoAssembly *
938 mono_domain_assembly_search (MonoAssemblyName *aname,
939                                                          gpointer user_data)
940 {
941         MonoDomain *domain = mono_domain_get ();
942         GSList *tmp;
943         MonoAssembly *ass;
944         gboolean refonly = GPOINTER_TO_UINT (user_data);
945
946         mono_domain_assemblies_lock (domain);
947         for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) {
948                 ass = tmp->data;
949                 /* Dynamic assemblies can't match here in MS.NET */
950                 if (ass->dynamic || refonly != ass->ref_only || !mono_assembly_names_equal (aname, &ass->aname))
951                         continue;
952
953                 mono_domain_assemblies_unlock (domain);
954                 return ass;
955         }
956         mono_domain_assemblies_unlock (domain);
957
958         return NULL;
959 }
960
961 MonoReflectionAssembly *
962 ves_icall_System_Reflection_Assembly_LoadFrom (MonoString *fname, MonoBoolean refOnly)
963 {
964         MonoDomain *domain = mono_domain_get ();
965         char *name, *filename;
966         MonoImageOpenStatus status = MONO_IMAGE_OK;
967         MonoAssembly *ass;
968
969         MONO_ARCH_SAVE_REGS;
970
971         if (fname == NULL) {
972                 MonoException *exc = mono_get_exception_argument_null ("assemblyFile");
973                 mono_raise_exception (exc);
974         }
975                 
976         name = filename = mono_string_to_utf8 (fname);
977
978         ass = mono_assembly_open_full (filename, &status, refOnly);
979         
980         if (!ass){
981                 MonoException *exc;
982
983                 if (status == MONO_IMAGE_IMAGE_INVALID)
984                         exc = mono_get_exception_bad_image_format (name);
985                 else
986                         exc = mono_get_exception_file_not_found (fname);
987                 g_free (name);
988                 mono_raise_exception (exc);
989         }
990
991         g_free (name);
992
993         return mono_assembly_get_object (domain, ass);
994 }
995
996 MonoReflectionAssembly *
997 ves_icall_System_AppDomain_LoadAssemblyRaw (MonoAppDomain *ad, 
998                                                                                         MonoArray *raw_assembly,
999                                                                                         MonoArray *raw_symbol_store, MonoObject *evidence,
1000                                                                                         MonoBoolean refonly)
1001 {
1002         MonoAssembly *ass;
1003         MonoReflectionAssembly *refass = NULL;
1004         MonoDomain *domain = ad->data;
1005         MonoImageOpenStatus status;
1006         guint32 raw_assembly_len = mono_array_length (raw_assembly);
1007         MonoImage *image = mono_image_open_from_data_full (mono_array_addr (raw_assembly, gchar, 0), raw_assembly_len, TRUE, NULL, refonly);
1008
1009         if (raw_symbol_store)
1010                 mono_raise_exception (mono_get_exception_not_implemented ("LoadAssemblyRaw: Raw Symbol Store not Implemented"));
1011   
1012         if (!image) {
1013                 mono_raise_exception (mono_get_exception_bad_image_format (""));
1014                 return NULL;
1015         }
1016
1017         ass = mono_assembly_load_from_full (image, "", &status, refonly);
1018
1019         if (!ass) {
1020                 mono_image_close (image);
1021                 mono_raise_exception (mono_get_exception_bad_image_format (""));
1022                 return NULL; 
1023         }
1024
1025         refass = mono_assembly_get_object (domain, ass);
1026         MONO_OBJECT_SETREF (refass, evidence, evidence);
1027         return refass;
1028 }
1029
1030 MonoReflectionAssembly *
1031 ves_icall_System_AppDomain_LoadAssembly (MonoAppDomain *ad,  MonoString *assRef, MonoObject *evidence, MonoBoolean refOnly)
1032 {
1033         MonoDomain *domain = ad->data; 
1034         MonoImageOpenStatus status = MONO_IMAGE_OK;
1035         MonoAssembly *ass;
1036         MonoAssemblyName aname;
1037         MonoReflectionAssembly *refass = NULL;
1038         gchar *name;
1039         gboolean parsed;
1040
1041         MONO_ARCH_SAVE_REGS;
1042
1043         g_assert (assRef != NULL);
1044
1045         name = mono_string_to_utf8 (assRef);
1046         parsed = mono_assembly_name_parse (name, &aname);
1047         g_free (name);
1048
1049         if (!parsed) {
1050                 MonoException *exc;
1051
1052                 /* This is a parse error... */
1053                 exc = mono_get_exception_file_not_found (assRef);
1054                 mono_raise_exception (exc);
1055         }
1056
1057         ass = mono_assembly_load_full (&aname, NULL, &status, refOnly);
1058         mono_assembly_name_free (&aname);
1059
1060         if (!ass && (refass = try_assembly_resolve (domain, assRef, refOnly)) == NULL){
1061                 /* FIXME: it doesn't make much sense since we really don't have a filename ... */
1062                 MonoException *exc = mono_get_exception_file_not_found (assRef);
1063                 mono_raise_exception (exc);
1064         }
1065
1066         if (refass == NULL)
1067                 refass = mono_assembly_get_object (domain, ass);
1068
1069         MONO_OBJECT_SETREF (refass, evidence, evidence);
1070         return refass;
1071 }
1072
1073 void
1074 ves_icall_System_AppDomain_InternalUnload (gint32 domain_id)
1075 {
1076         MonoDomain * domain = mono_domain_get_by_id (domain_id);
1077
1078         MONO_ARCH_SAVE_REGS;
1079
1080         if (NULL == domain) {
1081                 MonoException *exc = mono_get_exception_execution_engine ("Failed to unload domain, domain id not found");
1082                 mono_raise_exception (exc);
1083         }
1084         
1085         if (domain == mono_get_root_domain ()) {
1086                 mono_raise_exception (mono_get_exception_cannot_unload_appdomain ("The default appdomain can not be unloaded."));
1087                 return;
1088         }
1089
1090         /* 
1091          * Unloading seems to cause problems when running NUnit/NAnt, hence
1092          * this workaround.
1093          */
1094         if (g_getenv ("MONO_NO_UNLOAD"))
1095                 return;
1096
1097         mono_domain_unload (domain);
1098 }
1099
1100 gboolean
1101 ves_icall_System_AppDomain_InternalIsFinalizingForUnload (gint32 domain_id)
1102 {
1103         MonoDomain *domain = mono_domain_get_by_id (domain_id);
1104
1105         if (!domain)
1106                 return TRUE;
1107
1108         return mono_domain_is_unloading (domain);
1109 }
1110
1111 gint32
1112 ves_icall_System_AppDomain_ExecuteAssembly (MonoAppDomain *ad, MonoString *file, 
1113                                             MonoObject *evidence, MonoArray *args)
1114 {
1115         MonoAssembly *assembly;
1116         MonoImage *image;
1117         MonoMethod *method;
1118         char *filename;
1119         gint32 res;
1120         MonoReflectionAssembly *refass;
1121
1122         MONO_ARCH_SAVE_REGS;
1123
1124         filename = mono_string_to_utf8 (file);
1125         assembly = mono_assembly_open (filename, NULL);
1126         g_free (filename);
1127
1128         if (!assembly)
1129                 mono_raise_exception (mono_get_exception_file_not_found (file));
1130
1131         image = assembly->image;
1132
1133         method = mono_get_method (image, mono_image_get_entry_point (image), NULL);
1134
1135         if (!method)
1136                 g_error ("No entry point method found in %s", image->name);
1137
1138         if (!args)
1139                 args = (MonoArray *) mono_array_new (ad->data, mono_defaults.string_class, 0);
1140
1141         refass = mono_assembly_get_object (ad->data, assembly);
1142         MONO_OBJECT_SETREF (refass, evidence, evidence);
1143
1144         res = mono_runtime_exec_main (method, (MonoArray *)args, NULL);
1145
1146         return res;
1147 }
1148
1149 gint32 
1150 ves_icall_System_AppDomain_GetIDFromDomain (MonoAppDomain * ad) 
1151 {
1152         MONO_ARCH_SAVE_REGS;
1153
1154         return ad->data->domain_id;
1155 }
1156
1157 MonoAppDomain * 
1158 ves_icall_System_AppDomain_InternalSetDomain (MonoAppDomain *ad)
1159 {
1160         MonoDomain *old_domain = mono_domain_get();
1161
1162         MONO_ARCH_SAVE_REGS;
1163
1164         if (!mono_domain_set (ad->data, FALSE))
1165                 mono_raise_exception (mono_get_exception_appdomain_unloaded ());
1166
1167         return old_domain->domain;
1168 }
1169
1170 MonoAppDomain * 
1171 ves_icall_System_AppDomain_InternalSetDomainByID (gint32 domainid)
1172 {
1173         MonoDomain *current_domain = mono_domain_get ();
1174         MonoDomain *domain = mono_domain_get_by_id (domainid);
1175
1176         MONO_ARCH_SAVE_REGS;
1177
1178         if (!domain || !mono_domain_set (domain, FALSE))        
1179                 mono_raise_exception (mono_get_exception_appdomain_unloaded ());
1180
1181         return current_domain->domain;
1182 }
1183
1184 void
1185 ves_icall_System_AppDomain_InternalPushDomainRef (MonoAppDomain *ad)
1186 {
1187         MONO_ARCH_SAVE_REGS;
1188
1189         mono_thread_push_appdomain_ref (ad->data);
1190 }
1191
1192 void
1193 ves_icall_System_AppDomain_InternalPushDomainRefByID (gint32 domain_id)
1194 {
1195         MonoDomain *domain = mono_domain_get_by_id (domain_id);
1196
1197         MONO_ARCH_SAVE_REGS;
1198
1199         if (!domain)
1200                 /* 
1201                  * Raise an exception to prevent the managed code from executing a pop
1202                  * later.
1203                  */
1204                 mono_raise_exception (mono_get_exception_appdomain_unloaded ());
1205
1206         mono_thread_push_appdomain_ref (domain);
1207 }
1208
1209 void
1210 ves_icall_System_AppDomain_InternalPopDomainRef (void)
1211 {
1212         MONO_ARCH_SAVE_REGS;
1213
1214         mono_thread_pop_appdomain_ref ();
1215 }
1216
1217 MonoAppContext * 
1218 ves_icall_System_AppDomain_InternalGetContext ()
1219 {
1220         MONO_ARCH_SAVE_REGS;
1221
1222         return mono_context_get ();
1223 }
1224
1225 MonoAppContext * 
1226 ves_icall_System_AppDomain_InternalGetDefaultContext ()
1227 {
1228         MONO_ARCH_SAVE_REGS;
1229
1230         return mono_domain_get ()->default_context;
1231 }
1232
1233 MonoAppContext * 
1234 ves_icall_System_AppDomain_InternalSetContext (MonoAppContext *mc)
1235 {
1236         MonoAppContext *old_context = mono_context_get ();
1237
1238         MONO_ARCH_SAVE_REGS;
1239
1240         mono_context_set (mc);
1241
1242         return old_context;
1243 }
1244
1245 MonoString *
1246 ves_icall_System_AppDomain_InternalGetProcessGuid (MonoString* newguid)
1247 {
1248         MonoDomain* mono_root_domain = mono_get_root_domain ();
1249         mono_domain_lock (mono_root_domain);
1250         if (process_guid_set) {
1251                 mono_domain_unlock (mono_root_domain);
1252                 return mono_string_new_utf16 (mono_domain_get (), process_guid, sizeof(process_guid)/2);
1253         }
1254         memcpy (process_guid, mono_string_chars(newguid), sizeof(process_guid));
1255         process_guid_set = TRUE;
1256         mono_domain_unlock (mono_root_domain);
1257         return newguid;
1258 }
1259
1260 gboolean
1261 mono_domain_is_unloading (MonoDomain *domain)
1262 {
1263         if (domain->state == MONO_APPDOMAIN_UNLOADING || domain->state == MONO_APPDOMAIN_UNLOADED)
1264                 return TRUE;
1265         else
1266                 return FALSE;
1267 }
1268
1269 static void
1270 clear_cached_vtable (gpointer key, gpointer value, gpointer user_data)
1271 {
1272         MonoClass *klass = (MonoClass*)key;
1273         MonoDomain *domain = (MonoDomain*)user_data;
1274         MonoClassRuntimeInfo *runtime_info;
1275
1276         runtime_info = klass->runtime_info;
1277         if (runtime_info && runtime_info->max_domain >= domain->domain_id)
1278                 runtime_info->domain_vtables [domain->domain_id] = NULL;
1279 }
1280
1281 typedef struct unload_data {
1282         MonoDomain *domain;
1283         char *failure_reason;
1284 } unload_data;
1285
1286 static guint32 WINAPI
1287 unload_thread_main (void *arg)
1288 {
1289         unload_data *data = (unload_data*)arg;
1290         MonoDomain *domain = data->domain;
1291
1292         /* 
1293          * FIXME: Abort our parent thread last, so we can return a failure 
1294          * indication if aborting times out.
1295          */
1296         if (!mono_threads_abort_appdomain_threads (domain, 10000)) {
1297                 data->failure_reason = g_strdup_printf ("Aborting of threads in domain %s timed out.", domain->friendly_name);
1298                 return 1;
1299         }
1300
1301         /* Finalize all finalizable objects in the doomed appdomain */
1302         if (!mono_domain_finalize (domain, 10000)) {
1303                 data->failure_reason = g_strdup_printf ("Finalization of domain %s timed out.", domain->friendly_name);
1304                 return 1;
1305         }
1306
1307         /* Clear references to our vtables in class->runtime_info.
1308          * We also hold the loader lock because we're going to change
1309          * class->runtime_info.
1310          */
1311         mono_domain_lock (domain);
1312         mono_loader_lock ();
1313         g_hash_table_foreach (domain->class_vtable_hash, clear_cached_vtable, domain);
1314         mono_loader_unlock ();
1315         mono_domain_unlock (domain);
1316
1317         mono_threads_clear_cached_culture (domain);
1318
1319         domain->state = MONO_APPDOMAIN_UNLOADED;
1320
1321         /* printf ("UNLOADED %s.\n", domain->friendly_name); */
1322
1323         mono_domain_free (domain, FALSE);
1324
1325         mono_gc_collect (mono_gc_max_generation ());
1326
1327         return 0;
1328 }
1329
1330 /*
1331  * mono_domain_unload:
1332  * @domain: The domain to unload
1333  *
1334  *  Unloads an appdomain. Follows the process outlined in:
1335  *  http://blogs.gotdotnet.com/cbrumme
1336  *
1337  *  If doing things the 'right' way is too hard or complex, we do it the 
1338  *  'simple' way, which means do everything needed to avoid crashes and
1339  *  memory leaks, but not much else.
1340  */
1341 static void
1342 mono_domain_unload (MonoDomain *domain)
1343 {
1344         HANDLE thread_handle;
1345         gsize tid;
1346         guint32 res;
1347         MonoAppDomainState prev_state;
1348         MonoMethod *method;
1349         MonoObject *exc;
1350         unload_data thread_data;
1351         MonoDomain *caller_domain = mono_domain_get ();
1352
1353         /* printf ("UNLOAD STARTING FOR %s (%p) IN THREAD 0x%x.\n", domain->friendly_name, domain, GetCurrentThreadId ()); */
1354
1355         /* Atomically change our state to UNLOADING */
1356         prev_state = InterlockedCompareExchange ((gint32*)&domain->state,
1357                                                                                          MONO_APPDOMAIN_UNLOADING,
1358                                                                                          MONO_APPDOMAIN_CREATED);
1359         if (prev_state != MONO_APPDOMAIN_CREATED) {
1360                 if (prev_state == MONO_APPDOMAIN_UNLOADING)
1361                         mono_raise_exception (mono_get_exception_cannot_unload_appdomain ("Appdomain is already being unloaded."));
1362                 else
1363                         if (prev_state == MONO_APPDOMAIN_UNLOADED)
1364                                 mono_raise_exception (mono_get_exception_cannot_unload_appdomain ("Appdomain is already unloaded."));
1365                 else
1366                         g_assert_not_reached ();
1367         }
1368
1369         mono_domain_set (domain, FALSE);
1370         /* Notify OnDomainUnload listeners */
1371         method = mono_class_get_method_from_name (domain->domain->mbr.obj.vtable->klass, "DoDomainUnload", -1); 
1372         g_assert (method);
1373
1374         exc = NULL;
1375         mono_runtime_invoke (method, domain->domain, NULL, &exc);
1376         if (exc) {
1377                 /* Roll back the state change */
1378                 domain->state = MONO_APPDOMAIN_CREATED;
1379                 mono_domain_set (caller_domain, FALSE);
1380                 mono_raise_exception ((MonoException*)exc);
1381         }
1382
1383         thread_data.domain = domain;
1384         thread_data.failure_reason = NULL;
1385
1386         /* 
1387          * First we create a separate thread for unloading, since
1388          * we might have to abort some threads, including the current one.
1389          */
1390         /*
1391          * If we create a non-suspended thread, the runtime will hang.
1392          * See:
1393          * http://bugzilla.ximian.com/show_bug.cgi?id=27663
1394          */ 
1395 #if 0
1396         thread_handle = CreateThread (NULL, 0, unload_thread_main, &thread_data, 0, &tid);
1397 #else
1398         thread_handle = CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE)unload_thread_main, &thread_data, CREATE_SUSPENDED, &tid);
1399         ResumeThread (thread_handle);
1400 #endif
1401
1402         /* Wait for the thread */       
1403         while ((res = WaitForSingleObjectEx (thread_handle, INFINITE, TRUE) == WAIT_IO_COMPLETION)) {
1404                 if (mono_thread_has_appdomain_ref (mono_thread_current (), domain) && (mono_thread_interruption_requested ()))
1405                         /* The unload thread tries to abort us */
1406                         /* The icall wrapper will execute the abort */
1407                         CloseHandle (thread_handle);
1408                         return;
1409         }
1410         CloseHandle (thread_handle);
1411
1412         mono_domain_set (caller_domain, FALSE);
1413
1414         if (thread_data.failure_reason) {
1415                 MonoException *ex;
1416
1417                 /* Roll back the state change */
1418                 domain->state = MONO_APPDOMAIN_CREATED;
1419
1420                 g_warning (thread_data.failure_reason);
1421
1422                 ex = mono_get_exception_cannot_unload_appdomain (thread_data.failure_reason);
1423
1424                 g_free (thread_data.failure_reason);
1425                 thread_data.failure_reason = NULL;
1426
1427                 mono_raise_exception (ex);
1428         }
1429 }