Merge pull request #4503 from BrzVlad/fix-appdomain-unload
authorVlad Brezae <brezaevlad@gmail.com>
Fri, 24 Mar 2017 14:21:30 +0000 (16:21 +0200)
committerGitHub <noreply@github.com>
Fri, 24 Mar 2017 14:21:30 +0000 (16:21 +0200)
[runtime] Fix abort issues

mono/metadata/marshal.c
mono/metadata/object.c
mono/metadata/threads-types.h
mono/metadata/threads.c
mono/mini/mini-exceptions.c
mono/mini/mini-trampolines.c
mono/tests/abort-cctor-2.cs [new file with mode: 0644]
mono/tests/abort-cctor.cs

index af4cabd655fcf0665f9b4443ef9e363a6212ed2c..555a96122b7aec0a200cdae2863adecc460ba02a 100644 (file)
@@ -4427,7 +4427,7 @@ mono_marshal_get_runtime_invoke_dynamic (void)
        MonoMethodSignature *csig;
        MonoExceptionClause *clause;
        MonoMethodBuilder *mb;
-       int pos, posna;
+       int pos;
        char *name;
        WrapperInfo *info;
 
@@ -4497,15 +4497,6 @@ mono_marshal_get_runtime_invoke_dynamic (void)
        mono_mb_emit_byte (mb, CEE_LDNULL);
        mono_mb_emit_stloc (mb, 0);
 
-       /* Check for the abort exception */
-       mono_mb_emit_ldloc (mb, 1);
-       mono_mb_emit_op (mb, CEE_ISINST, mono_defaults.threadabortexception_class);
-       posna = mono_mb_emit_short_branch (mb, CEE_BRFALSE_S);
-
-       /* Delay the abort exception */
-       mono_mb_emit_icall (mb, ves_icall_System_Threading_Thread_ResetAbort);
-
-       mono_mb_patch_short_branch (mb, posna);
        mono_mb_emit_branch (mb, CEE_LEAVE);
 
        clause->handler_len = mono_mb_get_pos (mb) - clause->handler_offset;
index 3f3d6211638eb11c92b30ac7dd1f9bc5bf727e52..41c481d41097c862b4a715f246f4fd922af40c62 100644 (file)
@@ -355,7 +355,7 @@ mono_runtime_class_init_full (MonoVTable *vtable, MonoError *error)
        MonoNativeThreadId tid;
        int do_initialization = 0;
        MonoDomain *last_domain = NULL;
-       MonoException * pending_tae = NULL;
+       gboolean pending_tae = FALSE;
 
        error_init (error);
 
@@ -471,7 +471,7 @@ mono_runtime_class_init_full (MonoVTable *vtable, MonoError *error)
 
                mono_threads_begin_abort_protected_block ();
                mono_runtime_try_invoke (method, NULL, NULL, (MonoObject**) &exc, error);
-               gboolean got_pending_interrupt = mono_threads_end_abort_protected_block ();
+               mono_threads_end_abort_protected_block ();
 
                //exception extracted, error will be set to the right value later
                if (exc == NULL && !mono_error_ok (error))//invoking failed but exc was not set
@@ -519,13 +519,15 @@ mono_runtime_class_init_full (MonoVTable *vtable, MonoError *error)
                mono_coop_cond_broadcast (&lock->cond);
                mono_type_init_unlock (lock);
 
-               //This can happen if the cctor self-aborts
-               if (exc && mono_object_class (exc) == mono_defaults.threadabortexception_class)
-                       pending_tae = exc;
-
-               //TAEs are blocked around .cctors, they must escape as soon as no cctor is left to run.
-               if (!pending_tae && got_pending_interrupt)
-                       pending_tae = mono_thread_try_resume_interruption ();
+               /*
+                * This can happen if the cctor self-aborts. We need to reactivate tae
+                * (next interruption checkpoint will throw it) and make sure we won't
+                * throw tie for the type.
+                */
+               if (exc && mono_object_class (exc) == mono_defaults.threadabortexception_class) {
+                       pending_tae = TRUE;
+                       mono_thread_resume_interruption (FALSE);
+               }
        } else {
                /* this just blocks until the initializing thread is done */
                mono_type_init_lock (lock);
@@ -546,10 +548,8 @@ mono_runtime_class_init_full (MonoVTable *vtable, MonoError *error)
                vtable->initialized = 1;
        mono_type_initialization_unlock ();
 
-       //TAE wins over TIE
-       if (pending_tae)
-               mono_error_set_exception_instance (error, pending_tae);
-       else if (vtable->init_failed) {
+       /* If vtable init fails because of TAE, we don't throw TIE, only the TAE */
+       if (vtable->init_failed && !pending_tae) {
                /* Either we were the initializing thread or we waited for the initialization */
                mono_error_set_exception_instance (error, get_type_init_exception_for_vtable (vtable));
                return FALSE;
index 25571e7f896f7877aed0cb90fef41920d6320ffd..98786defa9c30cc5da22b79644c81ff874fe7a66 100644 (file)
@@ -227,7 +227,7 @@ uint32_t mono_alloc_special_static_data (uint32_t static_type, uint32_t size, ui
 void*    mono_get_special_static_data   (uint32_t offset);
 gpointer mono_get_special_static_data_for_thread (MonoInternalThread *thread, guint32 offset);
 
-MonoException* mono_thread_resume_interruption (void);
+MonoException* mono_thread_resume_interruption (gboolean exec);
 void mono_threads_perform_thread_dump (void);
 
 gboolean
@@ -253,7 +253,6 @@ mono_threads_detach_coop (gpointer cookie, gpointer *dummy);
 
 void mono_threads_begin_abort_protected_block (void);
 gboolean mono_threads_end_abort_protected_block (void);
-MonoException* mono_thread_try_resume_interruption (void);
 
 gboolean
 mono_thread_internal_current_is_attached (void);
index 6831de896320d1b723061a9924f7cc78b3e242ec..837cd2b842f3111b1964c6107f21ef2a355d00ba 100644 (file)
@@ -219,9 +219,18 @@ get_next_managed_thread_id (void)
        return InterlockedIncrement (&managed_thread_id_counter);
 }
 
+/*
+ * We separate interruptions/exceptions into either sync (they can be processed anytime,
+ * normally as soon as they are set, and are set by the same thread) and async (they can't
+ * be processed inside abort protected blocks and are normally set by other threads). We
+ * can have both a pending sync and async interruption. In this case, the sync exception is
+ * processed first. Since we clean sync flag first, mono_thread_execute_interruption must
+ * also handle all sync type exceptions before the async type exceptions.
+ */
 enum {
-       INTERRUPT_REQUESTED_BIT = 0x1,
-       INTERRUPT_REQUEST_DEFERRED_BIT = 0x2,
+       INTERRUPT_SYNC_REQUESTED_BIT = 0x1,
+       INTERRUPT_ASYNC_REQUESTED_BIT = 0x2,
+       INTERRUPT_REQUESTED_MASK = 0x3,
        ABORT_PROT_BLOCK_SHIFT = 2,
        ABORT_PROT_BLOCK_BITS = 8,
        ABORT_PROT_BLOCK_MASK = (((1 << ABORT_PROT_BLOCK_BITS) - 1) << ABORT_PROT_BLOCK_SHIFT)
@@ -234,146 +243,141 @@ mono_thread_get_abort_prot_block_count (MonoInternalThread *thread)
        return (state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT;
 }
 
-static void
-verify_thread_state (gsize state)
-{
-       //can't have both INTERRUPT_REQUESTED_BIT and INTERRUPT_REQUEST_DEFERRED_BIT set at the same time
-       g_assert ((state & (INTERRUPT_REQUESTED_BIT | INTERRUPT_REQUEST_DEFERRED_BIT)) != (INTERRUPT_REQUESTED_BIT | INTERRUPT_REQUEST_DEFERRED_BIT));
-
-       //XXX This would be nice to be true, but can happen due to self-aborts (and possibly set-pending-exception)
-       //if prot_count > 0, INTERRUPT_REQUESTED_BIT must never be set
-       // int prot_count = (state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT;
-       // g_assert (!(prot_count > 0 && (state & INTERRUPT_REQUESTED_BIT)));
-}
-
 void
 mono_threads_begin_abort_protected_block (void)
 {
        MonoInternalThread *thread = mono_thread_internal_current ();
        gsize old_state, new_state;
+       int new_val;
        do {
                old_state = thread->thread_state;
-               verify_thread_state (old_state);
-
-               int new_val = ((old_state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT) + 1;
-
-               new_state = 0;
-               if (old_state & (INTERRUPT_REQUESTED_BIT | INTERRUPT_REQUEST_DEFERRED_BIT)) {
-                       new_state |= INTERRUPT_REQUEST_DEFERRED_BIT;
-               }
 
+               new_val = ((old_state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT) + 1;
                //bounds check abort_prot_count
                g_assert (new_val > 0);
                g_assert (new_val < (1 << ABORT_PROT_BLOCK_BITS));
-               new_state |= new_val << ABORT_PROT_BLOCK_SHIFT;
 
+               new_state = old_state + (1 << ABORT_PROT_BLOCK_SHIFT);
        } while (InterlockedCompareExchangePointer ((volatile gpointer)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state);
+
+       /* Defer async request since we won't be able to process until exiting the block */
+       if (new_val == 1 && (new_state & INTERRUPT_ASYNC_REQUESTED_BIT)) {
+               InterlockedDecrement (&thread_interruption_requested);
+               THREADS_INTERRUPT_DEBUG ("[%d] begin abort protected block old_state %ld new_state %ld, defer tir %d\n", thread->small_id, old_state, new_state, thread_interruption_requested);
+               if (thread_interruption_requested < 0)
+                       g_warning ("bad thread_interruption_requested state");
+       } else {
+               THREADS_INTERRUPT_DEBUG ("[%d] begin abort protected block old_state %ld new_state %ld, tir %d\n", thread->small_id, old_state, new_state, thread_interruption_requested);
+       }
 }
 
-gboolean
-mono_threads_end_abort_protected_block (void)
+static gboolean
+mono_thread_state_has_interruption (gsize state)
 {
-       MonoInternalThread *thread = mono_thread_internal_current ();
-       gsize old_state, new_state;
-       do {
-               old_state = thread->thread_state;
-               verify_thread_state (old_state);
-
-               int new_val = ((old_state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT) - 1;
-               new_state = 0;
-
-               if ((old_state & INTERRUPT_REQUEST_DEFERRED_BIT) && new_val == 0) {
-                       new_state |= INTERRUPT_REQUESTED_BIT;
-               }
+       /* pending exception, self abort */
+       if (state & INTERRUPT_SYNC_REQUESTED_BIT)
+               return TRUE;
 
-               //bounds check abort_prot_count
-               g_assert (new_val >= 0);
-               g_assert (new_val < (1 << ABORT_PROT_BLOCK_BITS));
-               new_state |= new_val << ABORT_PROT_BLOCK_SHIFT;
+       /* abort, interruption, suspend */
+       if ((state & INTERRUPT_ASYNC_REQUESTED_BIT) && !(state & ABORT_PROT_BLOCK_MASK))
+               return TRUE;
 
-       } while (InterlockedCompareExchangePointer ((volatile gpointer)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state);
-       return (new_state & INTERRUPT_REQUESTED_BIT) == INTERRUPT_REQUESTED_BIT;
+       return FALSE;
 }
 
-
-//Don't use this function, use inc/dec below
-static void
-mono_thread_abort_prot_block_count_add (MonoInternalThread *thread, int val)
+gboolean
+mono_threads_end_abort_protected_block (void)
 {
+       MonoInternalThread *thread = mono_thread_internal_current ();
        gsize old_state, new_state;
+       int new_val;
        do {
                old_state = thread->thread_state;
-               verify_thread_state (old_state);
 
-               int new_val = val + ((old_state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT);
                //bounds check abort_prot_count
+               new_val = ((old_state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT) - 1;
                g_assert (new_val >= 0);
                g_assert (new_val < (1 << ABORT_PROT_BLOCK_BITS));
-               new_state = (old_state & ~ABORT_PROT_BLOCK_MASK) | (new_val << ABORT_PROT_BLOCK_SHIFT);
 
+               new_state = old_state - (1 << ABORT_PROT_BLOCK_SHIFT);
        } while (InterlockedCompareExchangePointer ((volatile gpointer)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state);
-}
 
-static void
-mono_thread_inc_abort_prot_block_count (MonoInternalThread *thread)
-{
-       mono_thread_abort_prot_block_count_add (thread, 1);
-}
+       if (new_val == 0 && (new_state & INTERRUPT_ASYNC_REQUESTED_BIT)) {
+               InterlockedIncrement (&thread_interruption_requested);
+               THREADS_INTERRUPT_DEBUG ("[%d] end abort protected block old_state %ld new_state %ld, restore tir %d\n", thread->small_id, old_state, new_state, thread_interruption_requested);
+       } else {
+               THREADS_INTERRUPT_DEBUG ("[%d] end abort protected block old_state %ld new_state %ld, tir %d\n", thread->small_id, old_state, new_state, thread_interruption_requested);
+       }
 
-static void
-mono_thread_dec_abort_prot_block_count (MonoInternalThread *thread)
-{
-       mono_thread_abort_prot_block_count_add (thread, -1);
+       return mono_thread_state_has_interruption (new_state);
 }
 
 static gboolean
 mono_thread_get_interruption_requested (MonoInternalThread *thread)
 {
        gsize state = thread->thread_state;
-       return (state & INTERRUPT_REQUESTED_BIT) == INTERRUPT_REQUESTED_BIT;
+
+       return mono_thread_state_has_interruption (state);
 }
 
-/* Returns TRUE is there was a state change */
+/*
+ * Returns TRUE is there was a state change
+ * We clear a single interruption request, sync has priority.
+ */
 static gboolean
 mono_thread_clear_interruption_requested (MonoInternalThread *thread)
 {
        gsize old_state, new_state;
        do {
                old_state = thread->thread_state;
-               verify_thread_state (old_state);
 
-               //Already cleared
-               if (!(old_state & (INTERRUPT_REQUESTED_BIT | INTERRUPT_REQUEST_DEFERRED_BIT)))
+               // no interruption to process
+               if (!(old_state & INTERRUPT_SYNC_REQUESTED_BIT) &&
+                               (!(old_state & INTERRUPT_ASYNC_REQUESTED_BIT) || (old_state & ABORT_PROT_BLOCK_MASK)))
                        return FALSE;
-               new_state = old_state & ~(INTERRUPT_REQUESTED_BIT | INTERRUPT_REQUEST_DEFERRED_BIT);
+
+               if (old_state & INTERRUPT_SYNC_REQUESTED_BIT)
+                       new_state = old_state & ~INTERRUPT_SYNC_REQUESTED_BIT;
+               else
+                       new_state = old_state & ~INTERRUPT_ASYNC_REQUESTED_BIT;
        } while (InterlockedCompareExchangePointer ((volatile gpointer)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state);
+
+       InterlockedDecrement (&thread_interruption_requested);
+       THREADS_INTERRUPT_DEBUG ("[%d] clear interruption old_state %ld new_state %ld, tir %d\n", thread->small_id, old_state, new_state, thread_interruption_requested);
+       if (thread_interruption_requested < 0)
+               g_warning ("bad thread_interruption_requested state");
        return TRUE;
 }
 
-/* Returns TRUE is there was a state change */
+/* Returns TRUE is there was a state change and the interruption can be processed */
 static gboolean
 mono_thread_set_interruption_requested (MonoInternalThread *thread)
 {
        //always force when the current thread is doing it to itself.
-       gboolean force_interrupt = thread == mono_thread_internal_current ();
+       gboolean sync = thread == mono_thread_internal_current ();
        gsize old_state, new_state;
        do {
                old_state = thread->thread_state;
-               verify_thread_state (old_state);
 
-               int prot_count = ((old_state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT);
                //Already set
-               if (old_state & (INTERRUPT_REQUESTED_BIT | INTERRUPT_REQUEST_DEFERRED_BIT))
+               if ((sync && (old_state & INTERRUPT_SYNC_REQUESTED_BIT)) ||
+                               (!sync && (old_state & INTERRUPT_ASYNC_REQUESTED_BIT)))
                        return FALSE;
 
-               //If there's an outstanding prot block, we queue it
-               if (prot_count && !force_interrupt) {
-                       new_state = old_state | INTERRUPT_REQUEST_DEFERRED_BIT;
-               } else
-                       new_state = old_state | INTERRUPT_REQUESTED_BIT;
+               if (sync)
+                       new_state = old_state | INTERRUPT_SYNC_REQUESTED_BIT;
+               else
+                       new_state = old_state | INTERRUPT_ASYNC_REQUESTED_BIT;
        } while (InterlockedCompareExchangePointer ((volatile gpointer)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state);
 
-       return (new_state & INTERRUPT_REQUESTED_BIT) == INTERRUPT_REQUESTED_BIT;
+       if (sync || !(new_state & ABORT_PROT_BLOCK_MASK)) {
+               InterlockedIncrement (&thread_interruption_requested);
+               THREADS_INTERRUPT_DEBUG ("[%d] set interruption on [%d] old_state %ld new_state %ld, tir %d\n", mono_thread_internal_current ()->small_id, thread->small_id, old_state, new_state, thread_interruption_requested);
+       } else {
+               THREADS_INTERRUPT_DEBUG ("[%d] set interruption on [%d] old_state %ld new_state %ld, tir deferred %d\n", mono_thread_internal_current ()->small_id, thread->small_id, old_state, new_state, thread_interruption_requested);
+       }
+
+       return sync || !(new_state & ABORT_PROT_BLOCK_MASK);
 }
 
 static inline MonoNativeThreadId
@@ -1164,8 +1168,7 @@ mono_thread_detach_internal (MonoInternalThread *thread)
        Leaving the counter unbalanced will cause a performance degradation since all threads
        will now keep checking their local flags all the time.
        */
-       if (mono_thread_clear_interruption_requested (thread))
-               InterlockedDecrement (&thread_interruption_requested);
+       mono_thread_clear_interruption_requested (thread);
 
        mono_threads_lock ();
 
@@ -4383,8 +4386,6 @@ mono_thread_execute_interruption (void)
 #ifdef HOST_WIN32
                WaitForSingleObjectEx (GetCurrentThread(), 0, TRUE);
 #endif
-               InterlockedDecrement (&thread_interruption_requested);
-
                /* Clear the interrupted flag of the thread so it can wait again */
                mono_thread_info_clear_self_interrupt ();
        }
@@ -4445,7 +4446,6 @@ mono_thread_request_interruption (gboolean running_managed)
 
        if (!mono_thread_set_interruption_requested (thread))
                return NULL;
-       InterlockedIncrement (&thread_interruption_requested);
 
        if (!running_managed || is_running_protected_wrapper ()) {
                /* Can't stop while in unmanaged code. Increase the global interruption
@@ -4470,7 +4470,7 @@ mono_thread_request_interruption (gboolean running_managed)
 /*This function should be called by a thread after it has exited all of
  * its handle blocks at interruption time.*/
 MonoException*
-mono_thread_resume_interruption (void)
+mono_thread_resume_interruption (gboolean exec)
 {
        MonoInternalThread *thread = mono_thread_internal_current ();
        gboolean still_aborting;
@@ -4485,15 +4485,17 @@ mono_thread_resume_interruption (void)
 
        /*This can happen if the protected block called Thread::ResetAbort*/
        if (!still_aborting)
-               return FALSE;
+               return NULL;
 
        if (!mono_thread_set_interruption_requested (thread))
                return NULL;
-       InterlockedIncrement (&thread_interruption_requested);
 
        mono_thread_info_self_interrupt ();
 
-       return mono_thread_execute_interruption ();
+       if (exec)
+               return mono_thread_execute_interruption ();
+       else
+               return NULL;
 }
 
 gboolean mono_thread_interruption_requested ()
@@ -4746,8 +4748,6 @@ async_abort_critical (MonoThreadInfo *info, gpointer ud)
        if (!mono_thread_set_interruption_requested (thread))
                return MonoResumeThread;
 
-       InterlockedIncrement (&thread_interruption_requested);
-
        ji = mono_thread_info_get_last_managed (info);
        protected_wrapper = ji && !ji->is_trampoline && !ji->async && mono_threads_is_critical_method (mono_jit_info_get_method (ji));
        running_managed = mono_jit_info_match (ji, MONO_CONTEXT_GET_IP (&mono_thread_info_get_suspend_state (info)->ctx));
@@ -4838,8 +4838,7 @@ async_suspend_critical (MonoThreadInfo *info, gpointer ud)
                        return KeepSuspended;
                }
        } else {
-               if (mono_thread_set_interruption_requested (thread))
-                       InterlockedIncrement (&thread_interruption_requested);
+               mono_thread_set_interruption_requested (thread);
                if (data->interrupt)
                        data->interrupt_token = mono_thread_info_prepare_interrupt ((MonoThreadInfo *)thread->thread_info);
 
@@ -5163,20 +5162,6 @@ mono_threads_detach_coop (gpointer cookie, gpointer *dummy)
        }
 }
 
-MonoException*
-mono_thread_try_resume_interruption (void)
-{
-       MonoInternalThread *thread;
-
-       thread = mono_thread_internal_current ();
-       if (!mono_get_eh_callbacks ()->mono_above_abort_threshold ())
-               return NULL;
-       if (mono_thread_get_abort_prot_block_count (thread) > 0 || mono_get_eh_callbacks ()->mono_current_thread_has_handle_block_guard ())
-               return NULL;
-
-       return mono_thread_resume_interruption ();
-}
-
 #if 0
 /* Returns TRUE if the current thread is ready to be interrupted. */
 gboolean
index 272065388e6187182d90d5d35fe36d8d1f6730c9..18fdc3cb847146614b09b2b087e02772d7f7e01d 100644 (file)
@@ -1987,7 +1987,7 @@ mono_handle_exception_internal (MonoContext *ctx, MonoObject *obj, gboolean resu
                                                if (is_outside) {
                                                        jit_tls->handler_block_return_address = NULL;
                                                        jit_tls->handler_block = NULL;
-                                                       mono_thread_resume_interruption (); /*We ignore the exception here, it will be raised later*/
+                                                       mono_thread_resume_interruption (TRUE); /*We ignore the exception here, it will be raised later*/
                                                }
                                        }
 
index 88c89e75e960ba18203b2633f707debec28a04cc..737c6a3700255a9ac98f292227f6051d630790b0 100644 (file)
@@ -1292,7 +1292,7 @@ mono_handler_block_guard_trampoline (mgreg_t *regs, guint8 *code, gpointer *tram
        if (!resume_ip) /*this should not happen, but we should avoid crashing */
                exc = mono_get_exception_execution_engine ("Invalid internal state, resuming abort after handler block but no resume ip found");
        else
-               exc = mono_thread_resume_interruption ();
+               exc = mono_thread_resume_interruption (TRUE);
 
        if (exc) {
                mono_handle_exception (&ctx, (MonoObject *)exc);
diff --git a/mono/tests/abort-cctor-2.cs b/mono/tests/abort-cctor-2.cs
new file mode 100644 (file)
index 0000000..f1e7548
--- /dev/null
@@ -0,0 +1,54 @@
+using System;
+using System.Threading;
+
+public class Critical {
+       static Critical ()
+       {
+               Program.mre1.Set ();
+               Program.mre2.WaitOne ();
+               try {
+                       throw new Exception ();
+               } catch (Exception) {
+                       Console.WriteLine ("Catched exception in cctor");
+                       Program.catched_exception = true;
+               }
+       }
+}
+
+
+public class Program {
+       public static ManualResetEvent mre1 = new ManualResetEvent (false);
+       public static ManualResetEvent mre2 = new ManualResetEvent (false);
+
+       public static bool catched_exception, catched_abort;
+
+       public static int Main (string[] args)
+       {
+               Thread thread = new Thread (DoStuff);
+               thread.Start ();
+
+               mre1.WaitOne ();
+               thread.Abort ();
+               mre2.Set ();
+
+               thread.Join ();
+
+               if (!catched_exception)
+                       Environment.Exit (1);
+               if (!catched_abort)
+                       Environment.Exit (2);
+
+               Console.WriteLine ("done, all things good");
+               return 0;
+       }
+
+       public static void DoStuff ()
+       {
+               try {
+                       new Critical ();
+               } catch (ThreadAbortException) {
+                       Console.WriteLine ("Catched thread abort");
+                       Program.catched_abort = true;
+               }
+       }
+}
index 19c494c9d5c3b571978f93a87466a4b075fe24a7..7ff7a3b28e5143b073080de5dcf530737239a235 100644 (file)
@@ -185,6 +185,7 @@ class Driver
        static void Test3 ()
        {
                Console.WriteLine ("Test 3:");
+               bool catched_abort = false;
 
                Driver.mre1.Reset ();
                Driver.mre2.Reset ();
@@ -197,6 +198,7 @@ class Driver
                                Environment.Exit (7);
                        } catch (ThreadAbortException e) {
                                Console.WriteLine ("TEST 3: aborted {0}", e);
+                               catched_abort = true;
                        }
                });
 
@@ -210,12 +212,16 @@ class Driver
 
                thread.Join ();
 
+               // Did we catch the abort
+               if (!catched_abort)
+                       Environment.Exit (8);
+
                //is StaticConstructor2 viable?
                try {
                        IsStaticConstructor3Viable ();
                        Console.WriteLine ("StaticConstructor3 is viable");
                        /* A regular exception escaping the .cctor makes the type not usable */
-                       Environment.Exit (8);
+                       Environment.Exit (9);
                } catch (TypeInitializationException e) {
                        Console.WriteLine ("StaticConstructor3 not viable");
                }
@@ -262,9 +268,9 @@ class Driver
                new StaticConstructor4 ();
                Console.WriteLine ("IsStaticConstructor4Viable: Did it get to the end? {0} Did it catch an exception {1} and end of the finally block {2}", StaticConstructor4.gotToEnd, StaticConstructor4.caughtException, got_to_the_end_of_the_finally);
                if (!StaticConstructor4.gotToEnd) /* the TAE must not land during a .cctor */
-                       Environment.Exit (9);
-               if (StaticConstructor4.caughtException)
                        Environment.Exit (10);
+               if (StaticConstructor4.caughtException)
+                       Environment.Exit (11);
        }
 
        static void Test4 ()
@@ -305,7 +311,7 @@ class Driver
 
                if (!got_to_the_end_of_the_finally) { 
                        Console.WriteLine ("Did not get to the end of test 4 cctor");
-                       Environment.Exit (11);
+                       Environment.Exit (12);
                }
 
                //is StaticConstructor4viable?
@@ -314,11 +320,52 @@ class Driver
                        Console.WriteLine ("StaticConstructor4 is viable"); /* a TAE doesn't make a type unusable */
                } catch (TypeInitializationException  e) {
                        Console.WriteLine ("StaticConstructor4 not viable");
-                       Environment.Exit (12);
+                       Environment.Exit (13);
+               }
+       }
+
+
+       class StaticConstructor5 {
+               public static bool catched_exception = false;
+               static StaticConstructor5 ()
+               {
+                       Driver.mre1.Set ();
+                       Driver.mre2.WaitOne ();
+                       try {
+                               throw new Exception ();
+                       } catch (Exception) {
+                               Console.WriteLine ("Catched exception in cctor");
+                               catched_exception = true;
+                       }
                }
        }
 
+       static void Test5 ()
+       {
+               bool catched_abort = false;
+               Driver.mre1.Reset ();
+               Driver.mre2.Reset ();
+               Thread thread = new Thread (() => {
+                                       try {
+                                               new StaticConstructor5 ();
+                                       } catch (ThreadAbortException) {
+                                               Console.WriteLine ("Catched thread abort");
+                                               catched_abort = true;
+                                       }
+                               });
+               thread.Start ();
+
+               Driver.mre1.WaitOne ();
+               thread.Abort ();
+               Driver.mre2.Set ();
 
+               thread.Join ();
+
+               if (!StaticConstructor5.catched_exception)
+                       Environment.Exit (14);
+               if (!catched_abort)
+                       Environment.Exit (15);
+       }
 
        public static int Main ()
        {
@@ -326,7 +373,8 @@ class Driver
                Test2 ();
                Test3 ();
                Test4 ();
+               Test5 ();
                Console.WriteLine ("done, all things good");
                return 0;
        }
-}
\ No newline at end of file
+}