+2009-04-18 Mark Probst <mark.probst@gmail.com>
+
+ * icall-def.h: New icall for getting the thread abort exception
+ state for a thread.
+
+ * object.c, thread.c, object-internals.h: A thread's abort
+ exception state is now a GC handle. To get the object it stands
+ for, we must move it into the current app domain, if it's
+ different than the one where it originated from.
+
+ * appdomain.c: Bump corlib version.
+
+ * domain.c, domain-internals.h: New function for setting the
+ domain and migrate the thread abort exception or not.
+
2009-04-16 Rodrigo Kumpera <rkumpera@novell.com>
* metadata-verify.c: Add initial verification of the
* Changes which are already detected at runtime, like the addition
* of icalls, do not require an increment.
*/
-#define MONO_CORLIB_VERSION 74
+#define MONO_CORLIB_VERSION 75
typedef struct
{
void
mono_domain_code_foreach (MonoDomain *domain, MonoCodeManagerFunc func, void *user_data) MONO_INTERNAL;
+void
+mono_domain_set_internal_with_options (MonoDomain *domain, gboolean migrate_exception) MONO_INTERNAL;
+
/*
* Installs a new function which is used to return a MonoJitInfo for a method inside
* an AOT module.
return GET_APPDOMAIN ();
}
+void
+mono_domain_set_internal_with_options (MonoDomain *domain, gboolean migrate_exception)
+{
+ MonoThread *thread;
+
+ SET_APPDOMAIN (domain);
+ SET_APPCONTEXT (domain->default_context);
+
+ if (migrate_exception) {
+ thread = mono_thread_current ();
+ if (!thread->abort_exc)
+ return;
+
+ g_assert (thread->abort_exc->object.vtable->domain != domain);
+ MONO_OBJECT_SETREF (thread, abort_exc, mono_get_exception_thread_abort ());
+ g_assert (thread->abort_exc->object.vtable->domain == domain);
+ }
+}
+
/**
* mono_domain_set_internal:
* @domain: the new domain
void
mono_domain_set_internal (MonoDomain *domain)
{
- SET_APPDOMAIN (domain);
- SET_APPCONTEXT (domain->default_context);
+ mono_domain_set_internal_with_options (domain, TRUE);
}
void
ICALL(THREAD_2, "ClrState", ves_icall_System_Threading_Thread_ClrState)
ICALL(THREAD_3, "CurrentThread_internal", mono_thread_current)
ICALL(THREAD_4, "FreeLocalSlotValues", mono_thread_free_local_slot_values)
+ICALL(THREAD_55, "GetAbortExceptionState", ves_icall_System_Threading_Thread_GetAbortExceptionState)
ICALL(THREAD_5, "GetCachedCurrentCulture", ves_icall_System_Threading_Thread_GetCachedCurrentCulture)
ICALL(THREAD_6, "GetCachedCurrentUICulture", ves_icall_System_Threading_Thread_GetCachedCurrentUICulture)
ICALL(THREAD_7, "GetDomainID", ves_icall_System_Threading_Thread_GetDomainID)
guint32 name_len;
guint32 state;
MonoException *abort_exc;
- MonoObject *abort_state;
+ int abort_state_handle;
guint64 tid; /* This is accessed as a gsize in the code (so it can hold a 64bit pointer on systems that need it), but needs to reserve 64 bits of space on all machines as it corresponds to a field in managed code */
HANDLE start_notify;
gpointer stack_ptr;
* will point into the next function in the executable, not this one.
*/
- if (((MonoObject*)ex)->vtable->klass == mono_defaults.threadabortexception_class)
- MONO_OBJECT_SETREF (mono_thread_current (), abort_exc, ex);
+ if (((MonoObject*)ex)->vtable->klass == mono_defaults.threadabortexception_class) {
+ MonoThread *thread = mono_thread_current ();
+ g_assert (ex->object.vtable->domain == mono_domain_get ());
+ MONO_OBJECT_SETREF (thread, abort_exc, ex);
+ if (thread->abort_state_handle)
+ mono_gchandle_free (thread->abort_state_handle);
+ thread->abort_state_handle = 0;
+ }
ex_handler (ex);
}
void ves_icall_System_Threading_Thread_Abort (MonoThread *thread, MonoObject *state) MONO_INTERNAL;
void ves_icall_System_Threading_Thread_ResetAbort (void) MONO_INTERNAL;
+MonoObject* ves_icall_System_Threading_Thread_GetAbortExceptionState (MonoThread *thread) MONO_INTERNAL;
void ves_icall_System_Threading_Thread_Suspend (MonoThread *thread) MONO_INTERNAL;
void ves_icall_System_Threading_Thread_Resume (MonoThread *thread) MONO_INTERNAL;
void ves_icall_System_Threading_Thread_ClrState (MonoThread *thread, guint32 state) MONO_INTERNAL;
DeleteCriticalSection (this->synch_cs);
g_free (this->synch_cs);
this->synch_cs = NULL;
+
+ if (this->abort_state_handle) {
+ g_assert (this->abort_exc);
+ mono_gchandle_free (this->abort_state_handle);
+ this->abort_state_handle = 0;
+ }
}
static void mono_thread_start (MonoThread *thread)
}
thread->state |= ThreadState_AbortRequested;
- MONO_OBJECT_SETREF (thread, abort_state, state);
+ if (thread->abort_state_handle)
+ mono_gchandle_free (thread->abort_state_handle);
+ if (state) {
+ thread->abort_state_handle = mono_gchandle_new (state, FALSE);
+ g_assert (thread->abort_state_handle);
+ } else {
+ thread->abort_state_handle = 0;
+ }
thread->abort_exc = NULL;
LeaveCriticalSection (thread->synch_cs);
mono_raise_exception (mono_get_exception_thread_state (msg));
} else {
thread->abort_exc = NULL;
- thread->abort_state = NULL;
+ if (thread->abort_state_handle) {
+ mono_gchandle_free (thread->abort_state_handle);
+ /* This is actually not necessary - the handle
+ only counts if the exception is set */
+ thread->abort_state_handle = 0;
+ }
}
LeaveCriticalSection (thread->synch_cs);
}
+static MonoObject*
+serialize_object (MonoObject *obj, gboolean *failure, MonoObject **exc)
+{
+ static MonoMethod *serialize_method;
+
+ void *params [1];
+ MonoObject *array;
+
+ if (!serialize_method) {
+ MonoClass *klass = mono_class_from_name (mono_defaults.corlib, "System.Runtime.Remoting", "RemotingServices");
+ serialize_method = mono_class_get_method_from_name (klass, "SerializeCallData", -1);
+ }
+
+ if (!serialize_method) {
+ *failure = TRUE;
+ return NULL;
+ }
+
+ g_assert (!obj->vtable->klass->marshalbyref);
+
+ params [0] = obj;
+ *exc = NULL;
+ array = mono_runtime_invoke (serialize_method, NULL, params, exc);
+ if (*exc)
+ *failure = TRUE;
+
+ return array;
+}
+
+static MonoObject*
+deserialize_object (MonoObject *obj, gboolean *failure, MonoObject **exc)
+{
+ static MonoMethod *deserialize_method;
+
+ void *params [1];
+ MonoObject *result;
+
+ if (!deserialize_method) {
+ MonoClass *klass = mono_class_from_name (mono_defaults.corlib, "System.Runtime.Remoting", "RemotingServices");
+ deserialize_method = mono_class_get_method_from_name (klass, "DeserializeCallData", -1);
+ }
+ if (!deserialize_method) {
+ *failure = TRUE;
+ return NULL;
+ }
+
+ params [0] = obj;
+ *exc = NULL;
+ result = mono_runtime_invoke (deserialize_method, NULL, params, exc);
+ if (*exc)
+ *failure = TRUE;
+
+ return result;
+}
+
+static MonoObject*
+make_transparent_proxy (MonoObject *obj, gboolean *failure, MonoObject **exc)
+{
+ static MonoMethod *get_proxy_method;
+
+ MonoDomain *domain = mono_domain_get ();
+ MonoRealProxy *real_proxy;
+ MonoReflectionType *reflection_type;
+ MonoTransparentProxy *transparent_proxy;
+
+ if (!get_proxy_method)
+ get_proxy_method = mono_class_get_method_from_name (mono_defaults.real_proxy_class, "GetTransparentProxy", 0);
+
+ g_assert (obj->vtable->klass->marshalbyref);
+
+ real_proxy = (MonoRealProxy*) mono_object_new (domain, mono_defaults.real_proxy_class);
+ reflection_type = mono_type_get_object (domain, &obj->vtable->klass->byval_arg);
+
+ real_proxy->class_to_proxy = reflection_type;
+ real_proxy->unwrapped_server = obj;
+
+ *exc = NULL;
+ transparent_proxy = (MonoTransparentProxy*) mono_runtime_invoke (get_proxy_method, real_proxy, NULL, exc);
+ if (*exc)
+ *failure = TRUE;
+
+ return (MonoObject*) transparent_proxy;
+}
+
+MonoObject*
+ves_icall_System_Threading_Thread_GetAbortExceptionState (MonoThread *thread)
+{
+ MonoObject *state, *serialized, *deserialized, *exc;
+ MonoDomain *domain;
+ gboolean failure = FALSE;
+
+ if (!thread->abort_state_handle)
+ return NULL;
+
+ state = mono_gchandle_get_target (thread->abort_state_handle);
+ g_assert (state);
+
+ domain = mono_domain_get ();
+ if (state->vtable->domain == domain)
+ return state;
+
+ if (state->vtable->klass->marshalbyref) {
+ deserialized = make_transparent_proxy (state, &failure, &exc);
+ } else {
+ mono_domain_set_internal_with_options (state->vtable->domain, FALSE);
+ serialized = serialize_object (state, &failure, &exc);
+ mono_domain_set_internal_with_options (domain, FALSE);
+ if (!failure)
+ deserialized = deserialize_object (serialized, &failure, &exc);
+ }
+
+ if (failure) {
+ MonoException *invalid_op_exc = mono_get_exception_invalid_operation ("Thread.ExceptionState cannot access an ExceptionState from a different AppDomain");
+ if (exc)
+ MONO_OBJECT_SETREF (invalid_op_exc, inner_ex, exc);
+ mono_raise_exception (invalid_op_exc);
+ }
+
+ return deserialized;
+}
+
static gboolean
mono_thread_suspend (MonoThread *thread)
{
+2009-04-18 Mark Probst <mark.probst@gmail.com>
+
+ * appdomain-thread-abort.cs: New tests for thread aborts across
+ app domains.
+
+ * Makefile.am: Test added.
+
2009-04-17 Sebastien Pouliot <sebastien@ximian.com>
* coreclr-security.cs: Add test case for the "special" case about
interlocked-3.cs \
interlocked-4.2.cs \
finalizer-wait.cs \
- critical-finalizers.cs
+ critical-finalizers.cs \
+ appdomain-thread-abort.cs
if AMD64
TEST_CS_SRC = $(BASE_TEST_CS_SRC) async-exc-compilation.cs
--- /dev/null
+using System;
+using System.Threading;
+using System.Runtime.Remoting;
+
+public class JustSomeClass {
+}
+
+public class Test : MarshalByRefObject {
+ ThreadAbortException exc;
+ public JustSomeClass other;
+
+ public void doThrow (int n, object state) {
+ if (n <= 0)
+ Thread.CurrentThread.Abort (state);
+ else
+ doThrow (n - 1, state);
+ }
+
+ public void abortProxy () {
+ doThrow (10, this);
+ }
+
+ public void abortOther () {
+ other = new JustSomeClass ();
+ doThrow (10, other);
+ }
+
+ public void abortString () {
+ try {
+ doThrow (10, "bla");
+ } catch (ThreadAbortException e) {
+ exc = e;
+ }
+ }
+
+ public void abortOtherIndirect (Test test) {
+ test.abortOther ();
+ }
+
+ public object getState () {
+ return exc.ExceptionState;
+ }
+
+ public int getInt () {
+ return 123;
+ }
+}
+
+public class main {
+ public static int Main (string [] args) {
+ AppDomain domain = AppDomain.CreateDomain ("newdomain");
+ Test test = (Test) domain.CreateInstanceAndUnwrap (typeof (Test).Assembly.FullName, typeof (Test).FullName);
+ bool didAbort;
+ Test testHere = new Test ();
+
+ if (!RemotingServices.IsTransparentProxy (test)) {
+ Console.WriteLine ("test is no proxy");
+ return 5;
+ }
+
+ try {
+ test.abortOtherIndirect (testHere);
+ } catch (ThreadAbortException e) {
+ object state = e.ExceptionState;
+ Thread.ResetAbort ();
+ if ((JustSomeClass)state != testHere.other) {
+ Console.WriteLine ("other class not preserved in state");
+ return 16;
+ }
+ }
+
+ try {
+ didAbort = false;
+ test.abortString ();
+ } catch (ThreadAbortException e) {
+ object state;
+ state = e.ExceptionState;
+ Thread.ResetAbort ();
+ didAbort = true;
+ if (state == null) {
+ Console.WriteLine ("state is null");
+ return 13;
+ } else {
+ if (RemotingServices.IsTransparentProxy (state)) {
+ Console.WriteLine ("state is proxy");
+ return 1;
+ }
+ if (!((string)state).Equals ("bla")) {
+ Console.WriteLine ("state is wrong: " + (string)state);
+ return 2;
+ }
+ }
+ if (RemotingServices.IsTransparentProxy (e)) {
+ Console.WriteLine ("exception is proxy");
+ return 3;
+ }
+ if (test.getState () != null) {
+ Console.WriteLine ("have state");
+ return 12;
+ }
+ }
+ if (!didAbort) {
+ Console.WriteLine ("no abort");
+ return 4;
+ }
+
+ try {
+ didAbort = false;
+ test.abortProxy ();
+ } catch (ThreadAbortException e) {
+ object state;
+ state = e.ExceptionState;
+ Thread.ResetAbort ();
+ didAbort = true;
+ if (state == null) {
+ Console.WriteLine ("state is null");
+ return 14;
+ } else {
+ if (!RemotingServices.IsTransparentProxy (state)) {
+ Console.WriteLine ("state is not proxy");
+ return 6;
+ }
+ if (((Test)state).getInt () != 123) {
+ Console.WriteLine ("state doesn't work");
+ return 15;
+ }
+ }
+ if (RemotingServices.IsTransparentProxy (e)) {
+ Console.WriteLine ("exception is proxy");
+ return 7;
+ }
+ }
+ if (!didAbort) {
+ Console.WriteLine ("no abort");
+ return 8;
+ }
+
+ try {
+ didAbort = false;
+ test.abortOther ();
+ } catch (ThreadAbortException e) {
+ object state = null;
+ bool stateExc = false;
+
+ didAbort = true;
+
+ try {
+ state = e.ExceptionState;
+ Console.WriteLine ("have state");
+ } catch (Exception) {
+ stateExc = true;
+ /* FIXME: if we put this after the try/catch, mono
+ quietly quits */
+ Thread.ResetAbort ();
+ }
+ if (!stateExc) {
+ Console.WriteLine ("no state exception");
+ return 9;
+ }
+
+ if (RemotingServices.IsTransparentProxy (e)) {
+ Console.WriteLine ("exception is proxy");
+ return 10;
+ }
+ }
+ if (!didAbort) {
+ Console.WriteLine ("no abort");
+ return 11;
+ }
+
+ Console.WriteLine ("done");
+
+ return 0;
+ }
+}