|
| BUG: Abort a Suspended Thread causes that thread hang |
|
|
|
|
| Messages |
|
Related Types |
This message was discovered on microsoft.public.dotnet.framework.clr.
| Ming Chen [MVP] |
| GOOD ANSWER |
Hi, When I try Aborting a Suspended thread in .NET, there is an exception thrown: System.Threading.ThreadStateException: Thread is suspended; attempting to abort. at System.Threading.Thread.AbortInternal() This is still fine, though it's already incosistent with .NET Document(MSDN: "If Abort is called on a thread that has been suspended, the thread is resumed and then aborted. "). The problem is after this exception, that thread is hang. It won't response to any function call include Resume, Suspend, Abort etc. And this hanging thread prevents the whole application from terminating normally. Here is a small test program which reproduces this problem (.NET Framework with Service Pack 2):
using System; using System.Threading;
public class AAA { public static void Go() { int i = 0; try { while(true) { ++ i; Thread.Sleep(500); Console.WriteLine("hello, " + i + " " + Thread.CurrentThread.ThreadState + ", " + Thread.CurrentThread.IsAlive); } } catch(Exception e) { Console.WriteLine("Thread Exception: " + e); } }
public static void Main() { Thread t = new Thread(new ThreadStart(Go)); Thread.CurrentThread.Name = "Main Thread"; t.Name = "Hang Thread"; try { t.Start(); Thread.Sleep(3000); Console.WriteLine("Suspending... " + t.ThreadState + " " + t.IsAlive); t.Suspend(); Thread.Sleep(1000); Console.WriteLine("Aborting... " + t.ThreadState + " " + t.IsAlive); t.Abort(); //Exception thrown here and the thread is lost. } catch(Exception e) { Console.WriteLine(e); Console.WriteLine("Exceptioning... " + t.ThreadState + " " + t.IsAlive); } Console.WriteLine("Resuming... " + t.ThreadState + " " + t.IsAlive); t.Resume(); //Called successfully but the thread will never executing or terminate } }
Best Regards Ming Chen
|
|
|
| |
|
|
| |
| |
| Ming Chen [MVP] |
| GOOD ANSWER |
A good thing about this bug is it could be reproduced with Rotor, which allows me to really do some debug. ;) In sscli\clr\src\vm\Threads.cpp, I fount following comments:
// What if someone else has this thread suspended already? It'll depend where the // thread got suspended. // // User Suspend: // We'll just set the abort bit and hope for the best on the resume. // // GC Suspend: // If it's suspended in jitted code, we'll hijack the IP. // If it's suspended but not in jitted code, we'll get suspended for GC, the GC // will complete, and then we'll abort the target thread. And // If the thread is user suspended (SyncSuspended) -- we're out of luck. Set the bit and // hope for the best on resume.
My understanding on those comments is for a suspended thread, Abort only sets the abort flag and leave the real job to Thread.Resume. And for some reasons, Abort would throw an exception after set that bit:
// ****** code from Rotor, threads.cpp line 4084 ********* if (m_State & TS_SyncSuspended) { ThreadStore::TrapReturningThreads(FALSE); ThreadStore::UnlockThreadStore(); COMPlusThrow(kThreadStateException, IDS_EE_THREAD_ABORT_WHILE_SUSPEND); _ASSERTE(0); // NOT REACHED }
In each thread operation (Resume, Suspend and Abort), CLR will first call API SuspendThread to suspend the physical thread to prevent it from moving (Thread.Suspend and Thread.Resume don't directly hook up with API SuspendThread and ResumeThread, thread class uses some events internally to accomplish such jobs instead of calling API). Then does the real operation. And before leaving those operation functions, CLR should call ResumeThread to resume the physical thread.
// ****** code from Rotor, threads.cpp line 4031 ********* // Win32 suspend the thread, so it isn't moving under us. #ifdef _DEBUG DWORD oldSuspendCount = (DWORD) #endif ::SuspendThread(hThread); // returns -1 on failure.
The Bad thing is, For Thread.Abort on a Suspended thread, that exception is thrown and control leaves that function without Resume the underling physical thread. So that thread is physically blocked at API level, any further .NET function calls (include Thread.Resume) won't resume it because they only perform event operations (SetEvent(m_SuspendEvent)). That thread is completely lost from .NET world.
Here is my fixes for Rotor: // ****** code from Rotor, threads.cpp line 4084 with fixes ********* if (m_State & TS_SyncSuspended) { // ThreadStore::TrapReturningThreads(FALSE); //mchen: comment this line out ::ResumeThread(hThread); //mchen: Resume the physical thread ThreadStore::UnlockThreadStore(); COMPlusThrow(kThreadStateException, IDS_EE_THREAD_ABORT_WHILE_SUSPEND); _ASSERTE(0); // NOT REACHED }
After these changes, the previous example passed and the thread is really aborted upon Resume function call.
Hope this helps. Ming Chen
"Ming Chen [MVP]" <Click here to reveal e-mail address> wrote in message news:uTgO286SCHA.1632@tkmsftngp11... [Original message clipped]
|
|
|
| |
|
|
| |
|
|
|
|
|
|
|
|
BootFX
Reliable and powerful .NET application framework. |
|
|
|
|
|
|