+
+gboolean
+mono_domain_is_unloading (MonoDomain *domain)
+{
+ if (domain->state == MONO_APPDOMAIN_UNLOADING || domain->state == MONO_APPDOMAIN_UNLOADED)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+static void
+clear_cached_vtable (gpointer key, gpointer value, gpointer user_data)
+{
+ MonoClass *klass = (MonoClass*)key;
+ MonoDomain *domain = (MonoDomain*)user_data;
+ MonoVTable *vt;
+
+ vt = klass->cached_vtable;
+ if (vt && vt->domain == domain)
+ klass->cached_vtable = NULL;
+}
+
+typedef struct unload_data {
+ MonoDomain *domain;
+ char *failure_reason;
+} unload_data;
+
+static guint32
+unload_thread_main (void *arg)
+{
+ unload_data *data = (unload_data*)arg;
+ MonoDomain *domain = data->domain;
+
+ /*
+ * FIXME: Abort our parent thread last, so we can return a failure
+ * indication if aborting times out.
+ */
+ if (!mono_threads_abort_appdomain_threads (domain, 10000)) {
+ data->failure_reason = g_strdup_printf ("Aborting of threads in domain %s timed out.", domain->friendly_name);
+ return 1;
+ }
+
+ /* Finalize all finalizable objects in the doomed appdomain */
+ if (!mono_domain_finalize (domain, 10000)) {
+ data->failure_reason = g_strdup_printf ("Finalization of domain %s timed out.", domain->friendly_name);
+ return 1;
+ }
+
+ /* Clear references to our vtables in class->cached_vtable */
+ mono_domain_lock (domain);
+ mono_g_hash_table_foreach (domain->class_vtable_hash, clear_cached_vtable,
+ domain);
+ mono_domain_unlock (domain);
+
+ domain->state = MONO_APPDOMAIN_UNLOADED;
+
+ //printf ("UNLOADED %s.\n", domain->friendly_name);
+
+ mono_domain_free (domain, FALSE);
+
+#ifdef HAVE_BOEHM_GC
+ GC_gcollect ();
+#endif
+
+ return 0;
+}
+
+/*
+ * mono_domain_unload:
+ *
+ * Unloads an appdomain. Follows the process outlined in:
+ * http://blogs.gotdotnet.com/cbrumme
+ * If doing things the 'right' way is too hard or complex, we do it the
+ * 'simple' way, which means do everything needed to avoid crashes and
+ * memory leaks, but not much else.
+ */
+static void
+mono_domain_unload (MonoDomain *domain)
+{
+ HANDLE thread_handle;
+ guint32 tid;
+ MonoAppDomainState prev_state;
+ MonoMethod *method;
+ MonoObject *exc;
+ unload_data thread_data;
+
+ //printf ("UNLOAD STARTING FOR %s.\n", domain->friendly_name);
+
+ /* Atomically change our state to UNLOADING */
+ prev_state = InterlockedCompareExchange (&domain->state,
+ MONO_APPDOMAIN_UNLOADING,
+ MONO_APPDOMAIN_CREATED);
+ if (prev_state != MONO_APPDOMAIN_CREATED) {
+ if (prev_state == MONO_APPDOMAIN_UNLOADING)
+ mono_raise_exception (mono_get_exception_cannot_unload_appdomain ("Appdomain is already being unloaded."));
+ else
+ if (prev_state == MONO_APPDOMAIN_UNLOADED)
+ mono_raise_exception (mono_get_exception_cannot_unload_appdomain ("Appdomain is already unloaded."));
+ else
+ g_assert_not_reached ();
+ }
+
+ /* Notify OnDomainUnload listeners */
+ method = look_for_method_by_name (domain->domain->mbr.obj.vtable->klass, "DoDomainUnload");
+ g_assert (method);
+
+ exc = NULL;
+ mono_runtime_invoke (method, domain->domain, NULL, &exc);
+ if (exc) {
+ /* Roll back the state change */
+ domain->state = MONO_APPDOMAIN_CREATED;
+ mono_raise_exception ((MonoException*)exc);
+ }
+
+ thread_data.domain = domain;
+ thread_data.failure_reason = NULL;
+
+ /*
+ * First we create a separate thread for unloading, since
+ * we might have to abort some threads, including the current one.
+ */
+ /*
+ * If we create a non-suspended thread, the runtime will hang.
+ * See:
+ * http://bugzilla.ximian.com/show_bug.cgi?id=27663
+ */
+#if 0
+ thread_handle = CreateThread (NULL, 0, unload_thread_main, &thread_data, 0, &tid);
+#else
+ thread_handle = CreateThread (NULL, 0, unload_thread_main, &thread_data, CREATE_SUSPENDED, &tid);
+ ResumeThread (thread_handle);
+#endif
+
+ while (WaitForSingleObjectEx (thread_handle, INFINITE, FALSE) == WAIT_IO_COMPLETION)
+ ; /* wait for the thread */
+
+
+ if (thread_data.failure_reason) {
+ MonoException *ex;
+
+ ex = mono_get_exception_cannot_unload_appdomain (thread_data.failure_reason);
+ /* Roll back the state change */
+ domain->state = MONO_APPDOMAIN_CREATED;
+
+ g_warning (thread_data.failure_reason);
+
+ g_free (thread_data.failure_reason);
+
+ mono_raise_exception (ex);
+ }
+}