Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[runtime] AppDomain fixes #5594

Merged
merged 6 commits into from Sep 22, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions mono/metadata/class-internals.h
Expand Up @@ -1170,6 +1170,8 @@ GENERATE_GET_CLASS_WITH_CACHE_DECL (variant)

#endif

GENERATE_GET_CLASS_WITH_CACHE_DECL (appdomain_unloaded_exception)

extern MonoDefaults mono_defaults;

void
Expand Down
2 changes: 1 addition & 1 deletion mono/metadata/gc.c
Expand Up @@ -1009,7 +1009,7 @@ mono_gc_cleanup (void)
mono_gc_suspend_finalizers ();

/* Try to abort the thread, in the hope that it is running managed code */
mono_thread_internal_abort (gc_thread);
mono_thread_internal_abort (gc_thread, FALSE);

/* Wait for it to stop */
ret = guarded_wait (gc_thread->handle, 100, FALSE);
Expand Down
3 changes: 3 additions & 0 deletions mono/metadata/loader.c
Expand Up @@ -76,6 +76,9 @@ MonoNativeTlsKey loader_lock_nest_id;
static void dllmap_cleanup (void);
static void cached_module_cleanup(void);

/* Class lazy loading functions */
GENERATE_GET_CLASS_WITH_CACHE (appdomain_unloaded_exception, "System", "AppDomainUnloadedException")

static void
global_loader_data_lock (void)
{
Expand Down
1 change: 1 addition & 0 deletions mono/metadata/object-internals.h
Expand Up @@ -376,6 +376,7 @@ typedef struct {
typedef enum {
MONO_THREAD_FLAG_DONT_MANAGE = 1, // Don't wait for or abort this thread
MONO_THREAD_FLAG_NAME_SET = 2, // Thread name set from managed code
MONO_THREAD_FLAG_APPDOMAIN_ABORT = 4, // Current requested abort originates from appdomain unload
} MonoThreadFlags;

struct _MonoInternalThread {
Expand Down
9 changes: 8 additions & 1 deletion mono/metadata/object.c
Expand Up @@ -4587,7 +4587,14 @@ mono_unhandled_exception_checked (MonoObjectHandle exc, MonoError *error)
MonoObjectHandle current_appdomain_delegate = MONO_HANDLE_NEW (MonoObject, NULL);

MonoClass *klass = mono_handle_class (exc);
if (mono_class_has_parent (klass, mono_defaults.threadabortexception_class))
/*
* AppDomainUnloadedException don't behave like unhandled exceptions unless thrown from
* a thread started in unmanaged world.
* https://msdn.microsoft.com/en-us/library/system.appdomainunloadedexception(v=vs.110).aspx#Anchor_6
*/
if (klass == mono_defaults.threadabortexception_class ||
(klass == mono_class_get_appdomain_unloaded_exception_class () &&
mono_thread_info_current ()->runtime_thread))
return;

field = mono_class_get_field_from_name (mono_defaults.appdomain_class, "UnhandledException");
Expand Down
82 changes: 44 additions & 38 deletions mono/metadata/remoting.c
Expand Up @@ -21,6 +21,7 @@
#include "mono/metadata/exception.h"
#include "mono/metadata/debug-helpers.h"
#include "mono/metadata/reflection-internals.h"
#include "mono/metadata/assembly.h"

typedef enum {
MONO_MARSHAL_NONE, /* No marshalling needed */
Expand Down Expand Up @@ -52,6 +53,9 @@ typedef struct _MonoRemotingMethods MonoRemotingMethods;
static MonoObject *
mono_remoting_wrapper (MonoMethod *method, gpointer *params);

static MonoException *
mono_remoting_update_exception (MonoException *exc);

static gint32
mono_marshal_set_domain_by_id (gint32 id, MonoBoolean push);

Expand Down Expand Up @@ -206,6 +210,7 @@ mono_remoting_marshal_init (void)
register_icall (ves_icall_mono_marshal_xdomain_copy_value, "ves_icall_mono_marshal_xdomain_copy_value", "object object", FALSE);
register_icall (mono_marshal_xdomain_copy_out_value, "mono_marshal_xdomain_copy_out_value", "void object object", FALSE);
register_icall (mono_remoting_wrapper, "mono_remoting_wrapper", "object ptr ptr", FALSE);
register_icall (mono_remoting_update_exception, "mono_remoting_update_exception", "object object", FALSE);
register_icall (mono_upgrade_remote_class_wrapper, "mono_upgrade_remote_class_wrapper", "void object object", FALSE);

#ifndef DISABLE_JIT
Expand Down Expand Up @@ -425,6 +430,7 @@ mono_remoting_wrapper (MonoMethod *method, gpointer *params)

if (exc) {
error_init (&error);
exc = (MonoObject*) mono_remoting_update_exception ((MonoException*)exc);
mono_error_set_exception_instance (&error, (MonoException *)exc);
goto fail;
}
Expand All @@ -444,6 +450,37 @@ mono_remoting_wrapper (MonoMethod *method, gpointer *params)
return NULL;
}

/*
* Handles exception transformation at appdomain call boundary.
* Note this is called from target appdomain inside xdomain wrapper, but from
* source domain in the mono_remoting_wrapper slowpath.
*/
static MonoException *
mono_remoting_update_exception (MonoException *exc)
{
MonoInternalThread *thread;
MonoClass *klass = mono_object_get_class ((MonoObject*)exc);

/* Serialization error can only happen when still in the target appdomain */
if (!(mono_class_get_flags (klass) & TYPE_ATTRIBUTE_SERIALIZABLE)) {
MonoException *ret;
char *aname = mono_stringify_assembly_name (&klass->image->assembly->aname);
char *message = g_strdup_printf ("Type '%s' in Assembly '%s' is not marked as serializable", klass->name, aname);
ret = mono_get_exception_serialization (message);
g_free (aname);
g_free (message);
return ret;
}

thread = mono_thread_internal_current ();
if (mono_object_get_class ((MonoObject*)exc) == mono_defaults.threadabortexception_class &&
thread->flags & MONO_THREAD_FLAG_APPDOMAIN_ABORT) {
mono_thread_internal_reset_abort (thread);
return mono_get_exception_appdomain_unloaded ();
}

return exc;
}

/**
* mono_marshal_get_remoting_invoke:
Expand Down Expand Up @@ -671,8 +708,8 @@ mono_marshal_get_xappdomain_dispatch (MonoMethod *method, int *marshal_types, in
int i, j, param_index, copy_locals_base;
MonoClass *ret_class = NULL;
int loc_array=0, loc_return=0, loc_serialized_exc=0;
MonoExceptionClause *clauses, *main_clause, *serialization_clause;
int pos, pos_leave, pos_leave_serialization;
MonoExceptionClause *main_clause;
int pos, pos_leave;
gboolean copy_return;
WrapperInfo *info;

Expand Down Expand Up @@ -714,8 +751,7 @@ mono_marshal_get_xappdomain_dispatch (MonoMethod *method, int *marshal_types, in

/* try */

clauses = (MonoExceptionClause *)mono_image_alloc0 (method->klass->image, 2 * sizeof (MonoExceptionClause));
main_clause = &clauses [0];
main_clause = (MonoExceptionClause *)mono_image_alloc0 (method->klass->image, sizeof (MonoExceptionClause));
main_clause->try_offset = mono_mb_get_label (mb);

/* Clean the call context */
Expand Down Expand Up @@ -896,55 +932,25 @@ mono_marshal_get_xappdomain_dispatch (MonoMethod *method, int *marshal_types, in
/* handler code */
main_clause->handler_offset = mono_mb_get_label (mb);

/*
* We deserialize the exception in another try-catch so we can catch
* serialization failure exceptions.
*/
serialization_clause = &clauses [1];
serialization_clause->try_offset = mono_mb_get_label (mb);

mono_mb_emit_managed_call (mb, method_rs_serialize_exc, NULL);
mono_mb_emit_stloc (mb, loc_serialized_exc);
mono_mb_emit_ldarg (mb, 2);
mono_mb_emit_ldloc (mb, loc_serialized_exc);
mono_mb_emit_byte (mb, CEE_STIND_REF);
pos_leave_serialization = mono_mb_emit_branch (mb, CEE_LEAVE);

/* Serialization exception catch */
serialization_clause->flags = MONO_EXCEPTION_CLAUSE_NONE;
serialization_clause->try_len = mono_mb_get_pos (mb) - serialization_clause->try_offset;
serialization_clause->data.catch_class = mono_defaults.object_class;

/* handler code */
serialization_clause->handler_offset = mono_mb_get_label (mb);

/*
* If the serialization of the original exception failed we serialize the newly
* thrown exception, which should always succeed, passing it over to the calling
* domain.
*/
mono_mb_emit_icall (mb, mono_remoting_update_exception);
mono_mb_emit_op (mb, CEE_CASTCLASS, mono_defaults.exception_class);
mono_mb_emit_managed_call (mb, method_rs_serialize_exc, NULL);
mono_mb_emit_stloc (mb, loc_serialized_exc);
mono_mb_emit_ldarg (mb, 2);
mono_mb_emit_ldloc (mb, loc_serialized_exc);
mono_mb_emit_byte (mb, CEE_STIND_REF);
mono_mb_emit_branch (mb, CEE_LEAVE);

/* end serialization exception catch */
serialization_clause->handler_len = mono_mb_get_pos (mb) - serialization_clause->handler_offset;
mono_mb_patch_branch (mb, pos_leave_serialization);

mono_mb_emit_branch (mb, CEE_LEAVE);
/* end main catch */
main_clause->handler_len = mono_mb_get_pos (mb) - main_clause->handler_offset;
/* end catch */
mono_mb_patch_branch (mb, pos_leave);

if (copy_return)
mono_mb_emit_ldloc (mb, loc_return);

mono_mb_emit_byte (mb, CEE_RET);

mono_mb_set_clauses (mb, 2, clauses);
mono_mb_set_clauses (mb, 1, main_clause);
#endif

info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE);
Expand Down
2 changes: 1 addition & 1 deletion mono/metadata/threads-types.h
Expand Up @@ -189,7 +189,7 @@ void ves_icall_System_Runtime_Remoting_Contexts_Context_ReleaseContext (MonoAppC

MonoInternalThread *mono_thread_internal_current (void);

void mono_thread_internal_abort (MonoInternalThread *thread);
void mono_thread_internal_abort (MonoInternalThread *thread, gboolean appdomain_unload);
void mono_thread_internal_suspend_for_shutdown (MonoInternalThread *thread);

gboolean mono_thread_internal_has_appdomain_ref (MonoInternalThread *thread, MonoDomain *domain);
Expand Down
32 changes: 20 additions & 12 deletions mono/metadata/threads.c
Expand Up @@ -199,9 +199,6 @@ static gboolean shutting_down = FALSE;

static gint32 managed_thread_id_counter = 0;

/* Class lazy loading functions */
static GENERATE_GET_CLASS_WITH_CACHE (appdomain_unloaded_exception, "System", "AppDomainUnloadedException")

static void
mono_threads_lock (void)
{
Expand Down Expand Up @@ -2292,7 +2289,7 @@ mono_thread_current_check_pending_interrupt (void)
}

static gboolean
request_thread_abort (MonoInternalThread *thread, MonoObject *state)
request_thread_abort (MonoInternalThread *thread, MonoObject *state, gboolean appdomain_unload)
{
LOCK_THREAD (thread);

Expand All @@ -2309,6 +2306,11 @@ request_thread_abort (MonoInternalThread *thread, MonoObject *state)
}

thread->state |= ThreadState_AbortRequested;
if (appdomain_unload)
thread->flags |= MONO_THREAD_FLAG_APPDOMAIN_ABORT;
else
thread->flags &= ~MONO_THREAD_FLAG_APPDOMAIN_ABORT;

if (thread->abort_state_handle)
mono_gchandle_free (thread->abort_state_handle);
if (state) {
Expand All @@ -2333,7 +2335,7 @@ request_thread_abort (MonoInternalThread *thread, MonoObject *state)
void
ves_icall_System_Threading_Thread_Abort (MonoInternalThread *thread, MonoObject *state)
{
if (!request_thread_abort (thread, state))
if (!request_thread_abort (thread, state, FALSE))
return;

if (thread == mono_thread_internal_current ()) {
Expand All @@ -2351,11 +2353,11 @@ ves_icall_System_Threading_Thread_Abort (MonoInternalThread *thread, MonoObject
* \p thread MUST NOT be the current thread.
*/
void
mono_thread_internal_abort (MonoInternalThread *thread)
mono_thread_internal_abort (MonoInternalThread *thread, gboolean appdomain_unload)
{
g_assert (thread != mono_thread_internal_current ());

if (!request_thread_abort (thread, NULL))
if (!request_thread_abort (thread, NULL, appdomain_unload))
return;
async_abort_internal (thread, TRUE);
}
Expand All @@ -2364,17 +2366,23 @@ void
ves_icall_System_Threading_Thread_ResetAbort (MonoThread *this_obj)
{
MonoInternalThread *thread = mono_thread_internal_current ();
gboolean was_aborting;
gboolean was_aborting, is_domain_abort;

LOCK_THREAD (thread);
was_aborting = thread->state & ThreadState_AbortRequested;
thread->state &= ~ThreadState_AbortRequested;
is_domain_abort = thread->flags & MONO_THREAD_FLAG_APPDOMAIN_ABORT;

if (was_aborting && !is_domain_abort)
thread->state &= ~ThreadState_AbortRequested;
UNLOCK_THREAD (thread);

if (!was_aborting) {
const char *msg = "Unable to reset abort because no abort was requested";
mono_set_pending_exception (mono_get_exception_thread_state (msg));
return;
} else if (is_domain_abort) {
/* Silently ignore abort resets in unloading appdomains */
return;
}

mono_get_eh_callbacks ()->mono_clear_abort_threshold ();
Expand Down Expand Up @@ -2574,7 +2582,7 @@ mono_thread_stop (MonoThread *thread)
{
MonoInternalThread *internal = thread->internal_thread;

if (!request_thread_abort (internal, NULL))
if (!request_thread_abort (internal, NULL, FALSE))
return;

if (internal == mono_thread_internal_current ()) {
Expand Down Expand Up @@ -3222,7 +3230,7 @@ remove_and_abort_threads (gpointer key, gpointer value, gpointer user)
wait->num++;

THREAD_DEBUG (g_print ("%s: Aborting id: %"G_GSIZE_FORMAT"\n", __func__, (gsize)thread->tid));
mono_thread_internal_abort (thread);
mono_thread_internal_abort (thread, FALSE);
}

return TRUE;
Expand Down Expand Up @@ -3937,7 +3945,7 @@ mono_threads_abort_appdomain_threads (MonoDomain *domain, int timeout)
if (user_data.wait.num > 0) {
/* Abort the threads outside the threads lock */
for (i = 0; i < user_data.wait.num; ++i)
mono_thread_internal_abort (user_data.wait.threads [i]);
mono_thread_internal_abort (user_data.wait.threads [i], TRUE);

/*
* We should wait for the threads either to abort, or to leave the
Expand Down
2 changes: 1 addition & 1 deletion mono/mini/debugger-agent.c
Expand Up @@ -7691,7 +7691,7 @@ vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf)

tls->abort_requested = TRUE;

mono_thread_internal_abort (THREAD_TO_INTERNAL (thread));
mono_thread_internal_abort (THREAD_TO_INTERNAL (thread), FALSE);
mono_loader_unlock ();
break;
}
Expand Down
4 changes: 3 additions & 1 deletion mono/mini/mini-runtime.c
Expand Up @@ -890,7 +890,9 @@ mono_thread_abort (MonoObject *obj)
g_free (jit_tls);*/

if ((mono_runtime_unhandled_exception_policy_get () == MONO_UNHANDLED_POLICY_LEGACY) ||
(obj->vtable->klass == mono_defaults.threadabortexception_class)) {
(obj->vtable->klass == mono_defaults.threadabortexception_class) ||
((obj->vtable->klass) == mono_class_get_appdomain_unloaded_exception_class () &&
mono_thread_info_current ()->runtime_thread)) {
mono_thread_exit ();
} else {
mono_invoke_unhandled_exception_hook (obj);
Expand Down
4 changes: 3 additions & 1 deletion mono/tests/Makefile.am
Expand Up @@ -469,6 +469,7 @@ TESTS_CS_SRC= \
appdomain-unload-callback.cs \
appdomain-unload-doesnot-raise-pending-events.cs \
appdomain-unload-asmload.cs \
appdomain-unload-exception.cs \
unload-appdomain-on-shutdown.cs \
bug-47295.cs \
loader.cs \
Expand Down Expand Up @@ -1907,7 +1908,8 @@ test-process-exit:

# tests that expect a 1 exit code
TESTS_UNHANDLED_EXCEPTION_1_SRC = \
unhandled-exception-1.cs
unhandled-exception-1.cs \
unhandled-exception-9.cs

# tests that expect a 255 exit code
TESTS_UNHANDLED_EXCEPTION_255_SRC = \
Expand Down
35 changes: 35 additions & 0 deletions mono/tests/appdomain-unload-exception.cs
@@ -0,0 +1,35 @@
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;

class Driver
{
static void ThrowTP ()
{
ManualResetEvent mre = new ManualResetEvent (false);

ThreadPool.QueueUserWorkItem (_ => { try { throw new AppDomainUnloadedException (); } finally { mre.Set (); } });

if (!mre.WaitOne (5000))
Environment.Exit (1);

/* Wait for exception unwinding */
Thread.Sleep (500);
}

static void ThrowThread ()
{
Thread thread = new Thread (_ => { throw new AppDomainUnloadedException (); });
thread.Start ();
thread.Join ();
}

static int Main (string[] args)
{
ThrowTP ();
ThrowThread ();

return 0;
}
}