TerminateThread() and its consequence (Windows)

Usually when it is required to quit a thread from outside of its thread function, you declare a variable like shouldQuit, and it checks if the shouldQuit is set inside of the thread function. From outside of the thread function, you set the shouldQuit to tell the thread to quit.

However, if a function in a DLL is called inside of the thread function, and somehow it stucks in the DLL function, the thread function even can’t check whether the shouldQuit is set or not. Eventually, the DLL function may return, but in that case, it is abnormal. Then.. what can you do?
You don’t want to wait until it returns.

Then you are tempted to use TerminateThread() function.
However, MSDN warns its use. Let’s quick test what can happen.

// The one and only application object
CWinApp theApp;

using namespace std;

CEvent g_threadTerminatedEvent;
bool g_shouldStop;

UINT __cdecl myThreadFunc( LPVOID pParam );
void SetThreadName( DWORD dwThreadID, char* threadName);

int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
	int nRetCode = 0;

	// initialize MFC and print and error on failure
	if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
	{
		// TODO: change error code to suit your needs
		_tprintf(_T("Fatal Error: MFC initialization failed\n"));
		nRetCode = 1;
	}
	else
	{
		// TODO: code your application's behavior here.
		g_shouldStop = false;

		CWinThread *pThread = AfxBeginThread( myThreadFunc, NULL );
		HANDLE handleForThread = pThread->m_hThread;
		DWORD threadID = pThread->m_nThreadID;

		SetThreadName( threadID, "My Thread!");

		BOOL isSuccessful;
		DWORD exitCode, error;
		isSuccessful = GetExitCodeThread( handleForThread, &exitCode );
		if( !isSuccessful )
		{
			error = GetLastError();
		}

		DWORD result;
		Sleep( 10000 );
		
		//g_shouldStop = true;
		//result = WaitForSingleObject( g_threadTerminatedEvent, INFINITE );
		//switch( result )
		//{
		//case WAIT_OBJECT_0 :
		//	_tprintf(_T("Thread quits.\n" ));
		//	break;

		//case WAIT_ABANDONED :
		//case WAIT_TIMEOUT :
		//default :
		//	break;
		//}

		// Force quit
		isSuccessful = TerminateThread( handleForThread, exitCode );
		if( !isSuccessful )
		{
			error = GetLastError();
		}

		Sleep(5000);
		_tprintf(_T("Hmm.. is finished?\n") );
	}

	return nRetCode;
}

UINT __cdecl myThreadFunc( LPVOID pParam )
{
	int i = 0;
	while( !g_shouldStop )
	{
		_tprintf(_T("[%3d] Thread is running.\n"), (i++)%1000  );
	}

	_tprintf(_T("Bye bye...\n" ));

	g_threadTerminatedEvent.SetEvent();

	return TRUE;
}

#define MS_VC_EXCEPTION 0x406D1388

#pragma pack(push,8)
typedef struct tagTHREADNAME_INFO
{
   DWORD dwType; // Must be 0x1000.
   LPCSTR szName; // Pointer to name (in user addr space).
   DWORD dwThreadID; // Thread ID (-1=caller thread).
   DWORD dwFlags; // Reserved for future use, must be zero.
} THREADNAME_INFO;
#pragma pack(pop)

void SetThreadName( DWORD dwThreadID, char* threadName)
{
   Sleep(10);
   THREADNAME_INFO info;
   info.dwType = 0x1000;
   info.szName = threadName;
   info.dwThreadID = dwThreadID;
   info.dwFlags = 0;

   __try
   {
      RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info );
   }
   __except(EXCEPTION_EXECUTE_HANDLER)
   {
   }
}

Then when the TerminateThread() is called, the myThreadFunc thread is terminated. However, its main function is finished and the program itself is stucked.
Take a look at this.
force_quit

If you go ahead by hitting F5, you will have this problem.
error

I think it is better not to use TerminateThread() as MSDN suggests.
However, what if you should use it, but knows that this problem happens in advance?
Does anyone know how to solve this problem?

2 responses to this post.

  1. Posted by Pat on October 22, 2010 at 7:29 AM

    Hi,

    In the SetThreadName() function, why do you call the Sleep(10) function?

    I’ve seen this on other sites but Microsoft’s site does not have the Sleep(10). Is this for an undocumented problem?

    http://msdn.microsoft.com/en-us/library/xcb2z8hs(VS.71).aspx

    Thanks!

    Reply

    • Posted by jongampark on October 22, 2010 at 7:43 AM

      It was to help debugging. If I recall it correctly, I wanted to check something else in the debugger.
      Isn’t it apparent that it is not necessary?

      Reply

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: