首页
社区
课程
招聘
[讨论]《windows核心编程》第四版HandShake程序的BUG
2010-3-15 19:28 5914

[讨论]《windows核心编程》第四版HandShake程序的BUG

2010-3-15 19:28
5914
这几天看《widows核心编程》书上HandShake的代码,发现了一个问题,猜想程序可能会因此陷入死锁状态。
后经实际测试,发现这确实是一个BUG。再后来,修改了部分代码,BUG消失了。
特发出来与大家分享。有什么不对的地方,还希望大家能够指出。

widows核心编程(第4版)HandShake程序有一个BUG——

        如果在Request编辑框里输入字符串Server Shutdown,点击提交按钮,服务器线程会终止。
然后在编辑框里输入任意字符串,再次点击提交按钮时,由于服务器线程已经终止,所以事件
g_hevtResultReturned 不会被设置为通知状态,主线程陷入死锁。

本程序对其进行了改动。改动如下:
(1)主线程在提交请求之前,检查服务器线程是否已经终止。如果已经终止,则不进入等待函数。
否则,提交请求,并进入等待函数。
(2)为了让主线程能够检查服务器线程是否已经终止,将服务器线程句柄设置为全局变量。
(3)DialogBox返回之后,主线程仍然可能陷入死锁。原因同上。为此,添加检测服务器线程是否已经终止的代码。
如果服务器线程已经终止,则直接进入清理句柄步骤。
否则,发送字符串Server Shutdown,令其终止,待终止后再进入清理句柄步骤。
另一种修改方法是,只等待服务器线程终止,而不等待事件g_hevtResultReturned被设置为通知状态。

修改后的代码如下:
#include <windows.h>
#include <tchar.h>
#include <windowsX.h>

#include "Resource.h"

// This event is signaled when the client has a request for the server
HANDLE g_hevtRequestSubmitted;

// This event is signaled when the server has a result for the client
HANDLE g_hevtResultReturned;

// The buffer shared between the client and server threads
TCHAR  g_szSharedRequestAndResultBuffer[1024];

// The special value sent from the client that causes the 
// server thread to terminate cleanly.
TCHAR  g_szServerShutdown[] = _T("Server Shutdown");

// 服务器线程句柄
HANDLE g_hThreadServer;

///////////////////////////////////////////////////////////////////////////////


// This is the code executed by the server thread
DWORD WINAPI ServerThread(PVOID pvParam)
{
	
	// Assume that the server thread is to run forever
	BOOL fShutdown = FALSE;
	
	while ( !fShutdown )
	{
		
		// Wait for the client to submit a request
		WaitForSingleObject( g_hevtRequestSubmitted, INFINITE );
		
		// Check to see if the client wants the server to terminate
		fShutdown = 
			(lstrcmpi( g_szSharedRequestAndResultBuffer, g_szServerShutdown ) == 0);
		
		if (!fShutdown)
		{
			// Process the client's request (reverse the string)
			_tcsrev( g_szSharedRequestAndResultBuffer );
		}
		
		// Let the client process the request's result
		SetEvent( g_hevtResultReturned );
	}
	
	// The client wants us to shutdown, exit
	return 0;
}

///////////////////////////////////////////////////////////////////////////////


BOOL Dlg_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam)
{
	SendMessage( hwnd, WM_SETICON, TRUE,  (LPARAM) 
		LoadIcon( (HINSTANCE)hwnd, MAKEINTRESOURCE(IDI_HANDSHAKE) ) );	
	
	// Initialize the edit control with some test data request
	Edit_SetText( GetDlgItem( hwnd, IDC_REQUEST ), _T("Some test data") );
	
	return(TRUE);
}


///////////////////////////////////////////////////////////////////////////////


void Dlg_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
{
	
	switch (id)
	{
		
	case IDCANCEL:
		EndDialog( hwnd, id );
		break;
		
	case IDC_SUBMIT:  // Submit a request to the server thread
		
		//检查服务器线程是否已经终止
		DWORD ExitCode;
		GetExitCodeThread( g_hThreadServer, &ExitCode );
		if ( ExitCode!=STILL_ACTIVE )
		{
			break;
		}

		// Copy the request string into the shared data buffer
		Edit_GetText( GetDlgItem(hwnd, IDC_REQUEST), 
			g_szSharedRequestAndResultBuffer, 
            sizeof(g_szSharedRequestAndResultBuffer) );

		// Let the server thread know that a request is ready in the buffer
		SetEvent( g_hevtRequestSubmitted );
		
		// Wait for the server to process the request and give us the result
		WaitForSingleObject( g_hevtResultReturned, INFINITE );/*如果用户已经发送过终止字符串,则g_hevtResultReturned不会变为通知状态*/
		
		// Let the user know the result
		Edit_SetText( GetDlgItem(hwnd, IDC_RESULT), 
            g_szSharedRequestAndResultBuffer );
		
		break;
	}
}


///////////////////////////////////////////////////////////////////////////////



int WINAPI Dlg_Proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	
	switch (uMsg)
	{
		HANDLE_MSG(hwnd, WM_INITDIALOG, Dlg_OnInitDialog);
		HANDLE_MSG(hwnd, WM_COMMAND,    Dlg_OnCommand);
	}
	
	return(FALSE);
}

int WINAPI _tWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd )
{
	// Create & initialize the 2 nonsignaled, auto-reset events
	g_hevtRequestSubmitted = CreateEvent( NULL, FALSE, FALSE, NULL );
	g_hevtResultReturned   = CreateEvent( NULL, FALSE, FALSE, NULL );
	
	// Spawn the server thread
	DWORD dwThreadID;
	g_hThreadServer = CreateThread( NULL, 0, ServerThread, NULL, 0, &dwThreadID );
	
	// Execute the client thread's user-interface
	DialogBox( hInstance, MAKEINTRESOURCE(IDD_HANDSHAKE), NULL, Dlg_Proc );
	
	// 检查服务器线程是否已经终止
	DWORD ExitCode;
	GetExitCodeThread( g_hThreadServer, &ExitCode );
	if ( ExitCode!=STILL_ACTIVE )
	{
		goto _clean_up_everything;
	}

	// The client's UI is closing, have the server thread shutdown
	lstrcpy( g_szSharedRequestAndResultBuffer, g_szServerShutdown );
	SetEvent( g_hevtRequestSubmitted );
	
	// Wait for the server thread to acknowledge the shutdown AND
	// wait for the server thread to fully terminate
	HANDLE h[2];
	h[0] = g_hevtResultReturned;
	h[1] = g_hThreadServer;
	WaitForMultipleObjects( 2, h, TRUE, INFINITE );/*如果用户已经发送过终止字符串,则g_hevtResultReturned不会变为通知状态*/

	//添加一个标签
_clean_up_everything:	
	// Properly clean up everything
	CloseHandle( g_hThreadServer );      
	CloseHandle( g_hevtRequestSubmitted );      
	CloseHandle( g_hevtResultReturned );      
	
   // The client thread terminates with the whole process

	return 0;
}

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

上传的附件:
收藏
点赞6
打赏
分享
最新回复 (4)
雪    币: 440
活跃值: (82)
能力值: ( LV9,RANK:200 )
在线值:
发帖
回帖
粉丝
asdfslw 3 2010-3-17 11:13
3
0
怎么没有人关注呢?
自己顶下吧。
雪    币: 401
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
xiilin 2010-3-17 11:20
4
0
不是不想关注,是……《Windows核心编程》那本书把我给弄的差点疯掉……看不懂
雪    币: 70
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
小爬 2010-3-17 11:20
5
0
lz 看得很仔细,我来**下。
雪    币: 287
活跃值: (25)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
wdbg 2010-3-17 11:45
6
0
在Dlg_OnCommand中似乎也该用WaitForMultipleObjects或改为等待服务线程句柄。在调用GetExitCodeThread和WaitForSingleObject之间,服务线程可能退出,虽然概率极小
游客
登录 | 注册 方可回帖
返回