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