Fix for TCFServer life-cycle issues bug 10621 RCL_2_4
authorChad Peckham <chad.peckham@nokia.com>
Wed, 10 Feb 2010 14:32:22 -0600
branchRCL_2_4
changeset 916 6743933eec70
parent 915 3b26c948790f
child 922 fbd0e7c75ee1
Fix for TCFServer life-cycle issues bug 10621
connectivity/com.nokia.tcf/native/TCFNative/TCFClient/ClientManager.cpp
connectivity/com.nokia.tcf/native/TCFNative/TCFClient/ClientManager.h
connectivity/com.nokia.tcf/native/TCFNative/TCFClient/TCAPIConnectionJni.cpp
connectivity/com.nokia.tcf/src/com/nokia/tcf/impl/TCAPIConnection.java
--- a/connectivity/com.nokia.tcf/native/TCFNative/TCFClient/ClientManager.cpp	Wed Feb 10 13:45:43 2010 -0600
+++ b/connectivity/com.nokia.tcf/native/TCFNative/TCFClient/ClientManager.cpp	Wed Feb 10 14:32:22 2010 -0600
@@ -488,7 +488,32 @@
 	TCDEBUGLOGA1("CClientManager::StartServer end numRefs = %d\n", pData->numRefs);
 	return ret;
 }
-
+BOOL CClientManager::IsTCFServerActive(DWORD processId)
+{
+	HANDLE h = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, processId);
+	if (h)
+	{
+		// is it really still alive?
+		DWORD exitCode = -5;
+		BOOL exitCall = ::GetExitCodeProcess(h, &exitCode);
+		::CloseHandle(h);
+		if (exitCall == TRUE && exitCode != STILL_ACTIVE)
+		{
+			// TCFServer is really dead
+			return FALSE;
+		}
+		else
+		{
+			// TCFServer is still active
+			return TRUE;
+		}
+	}
+	else
+	{
+		// TCFServer is really dead
+		return FALSE;
+	}
+}
 long CClientManager::StopServer()
 {
 	long ret = TCAPI_ERR_NONE;
@@ -516,23 +541,34 @@
 		// substract ref count
 		pData->numRefs--;
 		if (pData->numRefs < 0) pData->numRefs = 0;
+
+		bool sendStop = true;
+		if (!IsTCFServerActive(pData->serverProcess.dwProcessId))
+		{
+			sendStop = false;
+			pData->numRefs = 0;
+		}
+
 		// if refcount == 0 then really stop the server process
 		if (pData->numRefs == 0)
 		{
-			// last client process is closing
-			// tell server to exit
-			ServerCommandData cmdrsp;
-			cmdrsp.command = eCmdExit;
-			
-			TCDEBUGLOGS(" SendCommand eCmdExit\n");
-			m_Server->SendCommand(&cmdrsp);
-			TCDEBUGLOGS(" GetResponse eExit\n");
-			m_Server->GetResponse(&cmdrsp);
-			
-			// wait for process to exit
-			TCDEBUGLOGS(" WaitForSingleObject start\n");
-			DWORD waitErr = ::WaitForSingleObject(m_hServer, 10000L /*INFINITE*/);
-			TCDEBUGLOGA1("CClientManager::StopServer WaitForSingleObject = %d\n", waitErr);
+			if (sendStop)
+			{
+				// last client process is closing
+				// tell server to exit
+				ServerCommandData cmdrsp;
+				cmdrsp.command = eCmdExit;
+				
+				TCDEBUGLOGS(" SendCommand eCmdExit\n");
+				m_Server->SendCommand(&cmdrsp);
+				TCDEBUGLOGS(" GetResponse eExit\n");
+				m_Server->GetResponse(&cmdrsp);
+				
+				// wait for process to exit
+				TCDEBUGLOGS(" WaitForSingleObject start\n");
+				DWORD waitErr = ::WaitForSingleObject(m_hServer, 10000L /*INFINITE*/);
+				TCDEBUGLOGA1("CClientManager::StopServer WaitForSingleObject = %d\n", waitErr);
+			}
 
 			// now close our handle to server process
 			if (m_hServer != NULL)
@@ -580,11 +616,14 @@
 BOOL CClientManager::IsServerRunning()
 {
 	pServerProcessData pData = m_Server->GetProcessPtr();
-	if (pData->serverProcess.hProcess != NULL)
+	if (IsTCFServerActive(pData->serverProcess.dwProcessId))
+	{
 		return TRUE;
+	}
 	else
+	{
 		return FALSE;
-
+	}
 }
 
 void CClientManager::CreateLockFile(DWORD processId)
@@ -640,8 +679,8 @@
 
 void CClientManager::DeleteFromLockFile(DWORD serverProcessId)
 {
-	DWORD callingId[10];
-	DWORD serverId[10];
+	DWORD creatorIds[10];
+	DWORD serverIds[10];
 	int numIds = 0;
 
 	DWORD ourProcessId = ::GetCurrentProcessId();
@@ -663,16 +702,16 @@
 				BOOL done = FALSE;
 				while (!done)
 				{
-					DWORD cId = 0xffffffff;
-					DWORD sId = 0xffffffff;
-					int n = fscanf(f, "%ld %ld\n", &cId, &sId);
+					DWORD creatorId = 0xffffffff;
+					DWORD serverId = 0xffffffff;
+					int n = fscanf(f, "%ld %ld\n", &creatorId, &serverId);
 					if (n == 2)
 					{
-						TCDEBUGLOGA3("CClientManager::DeleteFromLockFile numIds=%d sId=%d pId=%d\n", numIds, cId, sId);
-						if (cId != ourProcessId || sId != serverProcessId)
+						TCDEBUGLOGA3("CClientManager::DeleteFromLockFile numIds=%d creatorId=%d serverId=%d\n", numIds, creatorId, serverId);
+						if (creatorId != ourProcessId || serverId != serverProcessId)
 						{
-							callingId[numIds] = cId;
-							serverId[numIds] = sId;
+							creatorIds[numIds] = creatorId;
+							serverIds[numIds] = serverId;
 							numIds++;
 							if (numIds > 9)
 								done = TRUE;
@@ -695,7 +734,7 @@
 				{
 					for (int i = 0; i < numIds; i++)
 					{
-						fprintf(f, "%ld %ld\n", callingId[i], serverId[i]);
+						fprintf(f, "%ld %ld\n", creatorIds[i], serverIds[i]);
 					}
 					fclose(f);
 				}
@@ -708,8 +747,8 @@
 // we should not have more than a few Carbide processes connecting to the same TCFServer
 void CClientManager::TerminateServerThroughLockFile(pServerProcessData pData)
 {
-	DWORD callingId[10];
-	DWORD serverId[10];
+	DWORD creatorIds[10];
+	DWORD serverIds[10];
 	BOOL liveCaller[10];
 	int numIds = 0;
 	if (m_ServerLockFile != NULL)
@@ -729,14 +768,14 @@
 				BOOL done = FALSE;
 				while (!done)
 				{
-					DWORD cId = 0xffffffff;
-					DWORD sId = 0xffffffff;
-					int n = fscanf(f, "%ld %ld\n", &cId, &sId);
+					DWORD creatorId = 0xffffffff;
+					DWORD serverId = 0xffffffff;
+					int n = fscanf(f, "%ld %ld\n", &creatorId, &serverId);
 					if (n == 2)
 					{
-						TCDEBUGLOGA3("CClientManager::TerminateServerThroughLockFile n=%d sId=%d pId=%d\n", n, cId, sId);
-						callingId[numIds] = cId;
-						serverId[numIds] = sId;
+						TCDEBUGLOGA3("CClientManager::TerminateServerThroughLockFile n=%d creatorId=%d serverId=%d\n", n, creatorId, serverId);
+						creatorIds[numIds] = creatorId;
+						serverIds[numIds] = serverId;
 						numIds++;
 						if (numIds > 9)
 							done = TRUE;
@@ -751,28 +790,40 @@
 				int numDeadCallers = 0;
 				for (int i = 0; i < numIds; i++)
 				{
-					HANDLE h = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, callingId[i]);
+					HANDLE h = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, creatorIds[i]);
 					if (h)
 					{
 						// calling process is still alive
 						liveCaller[i] = TRUE;
+						DWORD exitCode = -5;
+						BOOL exitCall = ::GetExitCodeProcess(h, &exitCode);
+						DWORD id = ::GetCurrentProcessId();
 						::CloseHandle(h);
-						TCDEBUGLOGA1("CClientManager::TerminateServerThroughLockFile %d alive\n", callingId[i]);
+						TCDEBUGLOGA3("CClientManager::TerminateServerThroughLockFile %d alive exitCall=%d currentId=%d\n", creatorIds[i], exitCall, id);
+						if (exitCall == TRUE && exitCode != STILL_ACTIVE)
+						{
+							liveCaller[i] = FALSE;
+							numDeadCallers++;
+						}
+						{
+							TCDEBUGLOGA2("CClientManager::TerminateServerThroughLockFile exitCode=%d still_active=%d\n", exitCode, STILL_ACTIVE);
+						}
 					}
 					else
 					{
 						liveCaller[i] = FALSE;
 						numDeadCallers++;
 						DWORD err = ::GetLastError();
-						TCDEBUGLOGA3("CClientManager::TerminateServerThroughLockFile %d dead err=%d:%s\n", callingId[i], err, GetErrorText(err));
+						TCDEBUGLOGA3("CClientManager::TerminateServerThroughLockFile %d dead err=%d:%s\n", creatorIds[i], err, GetErrorText(err));
 					}
 				}
 				if (numDeadCallers == numIds)
 				{
+					// All clients of this TCFServer are dead
 					// terminate the TCFServer, and delete lock file
 					pData->numRefs = 0;
 					::remove(m_ServerLockFile);
-					HANDLE h = ::OpenProcess(SYNCHRONIZE|PROCESS_TERMINATE, FALSE, serverId[0]);
+					HANDLE h = ::OpenProcess(SYNCHRONIZE|PROCESS_TERMINATE, FALSE, serverIds[0]);
 					if (h)
 					{
 						BOOL ret = ::TerminateProcess(h, -1);
@@ -786,27 +837,43 @@
 				}
 				else
 				{
-					// leave TCFServer running, recreate lock file and save live callers
-					::remove(m_ServerLockFile);
-					f = fopen(m_ServerLockFile, "wt");
-					if (f)
+					// some java clients are still alive
+					//   check to see if TCFServer is still alive
+					if (IsTCFServerActive(serverIds[0]))
 					{
-						for (int i = 0; i < numIds; i++)
+						// TCFServer is still active
+						// leave TCFServer running, recreate lock file and save live callers
+						::remove(m_ServerLockFile);
+						f = fopen(m_ServerLockFile, "wt");
+						if (f)
 						{
-							if (liveCaller[i])
+							for (int i = 0; i < numIds; i++)
 							{
-								fprintf(f, "%ld %ld\n", callingId[i], serverId[i]);
+								if (liveCaller[i])
+								{
+									fprintf(f, "%ld %ld\n", creatorIds[i], serverIds[i]);
+								}
 							}
+							fclose(f);
 						}
-						fclose(f);
+						pData->numRefs -= numDeadCallers;
+						if (pData->numRefs < 0) pData->numRefs = 0;
 					}
-					pData->numRefs -= numDeadCallers;
-					if (pData->numRefs < 0) pData->numRefs = 0;
+					else
+					{
+						// TCFServer is really dead
+						pData->numRefs = 0;
+						::remove(m_ServerLockFile);
+					}
 				}
 			}
 			else
 			{
 				// error opening lock file
+				// perhaps the user deleted it,
+				//   if so, we assume he has also deleted the TCFServer as we now have no way of verifying if the 
+				//   process is dead.
+				pData->numRefs = 0;
 				DWORD err = ::GetLastError();
 				TCDEBUGLOGA2("CClientManager::TerminateServerThroughLockFile fopenErr=%d:%s\n", err, GetErrorText(err));
 			}
--- a/connectivity/com.nokia.tcf/native/TCFNative/TCFClient/ClientManager.h	Wed Feb 10 13:45:43 2010 -0600
+++ b/connectivity/com.nokia.tcf/native/TCFNative/TCFClient/ClientManager.h	Wed Feb 10 14:32:22 2010 -0600
@@ -70,6 +70,7 @@
 	void AppendToLockFile(DWORD processId);
 	void DeleteLockFile();
 	void DeleteFromLockFile(DWORD processId);
+	BOOL IsTCFServerActive(DWORD processId);
 
 	// input stream
 	CInputStream* FindInputStream(long inClientId);
--- a/connectivity/com.nokia.tcf/native/TCFNative/TCFClient/TCAPIConnectionJni.cpp	Wed Feb 10 13:45:43 2010 -0600
+++ b/connectivity/com.nokia.tcf/native/TCFNative/TCFClient/TCAPIConnectionJni.cpp	Wed Feb 10 14:32:22 2010 -0600
@@ -84,6 +84,13 @@
 	TCDEBUGOPEN();
 
 	TCDEBUGLOGS("nativeConnect\n");
+	if (!gManager->IsServerRunning())
+	{
+		TCDEBUGLOGS("Server not running\n");
+		TCDEBUGCLOSE();
+		return TCAPI_ERR_COMM_SERVER_RESPONSE_TIMEOUT;
+	}
+
 
 	gManager->m_Server->WaitforServerPipeAccess();
 
@@ -1255,6 +1262,13 @@
 	TCDEBUGOPEN();
 	TCDEBUGLOGS("nativeSendMessage\n");
 	TCDEBUGLOGA1(" inClientId=%d\n", inClientId);
+	if (!gManager->IsServerRunning())
+	{
+		// return right away if TCFServer is dead
+		TCDEBUGLOGS("nativeSendMessage: server is dead\n");
+		TCDEBUGCLOSE();
+		return TCAPI_ERR_COMM_SERVER_RESPONSE_TIMEOUT;
+	}
 
 	gManager->m_Server->WaitforServerPipeAccess();
 
--- a/connectivity/com.nokia.tcf/src/com/nokia/tcf/impl/TCAPIConnection.java	Wed Feb 10 13:45:43 2010 -0600
+++ b/connectivity/com.nokia.tcf/src/com/nokia/tcf/impl/TCAPIConnection.java	Wed Feb 10 14:32:22 2010 -0600
@@ -46,7 +46,12 @@
 			} catch (UnsatisfiedLinkError e) {
 				// if Carbide DLL is not found in DE, 
 				// try to load one from the plugin itself
-				System.loadLibrary("TCFClient");
+				try {
+					System.loadLibrary("TCFClient");
+				} catch (UnsatisfiedLinkError e2) {
+					// no native TCF, e.g., not on Windows or in a misconfigured dev layout
+					e2.printStackTrace();
+				}
 			}
 		}
 	}
@@ -354,6 +359,23 @@
 				this.cookie.setConnected(true);
 				this.connection = inConnection;
 				this.messageOptions = inMessageOptions;
+			} else if (ret == TCErrorConstants.TCAPI_ERR_COMM_SERVER_RESPONSE_TIMEOUT){
+				// TCFServer may have died, attempt to restart it
+				ret = nativeStartServer();
+				if (ret == TCErrorConstants.TCAPI_ERR_NONE) {
+					// now try connecting again
+					ret = nativeConnect(type, options, settings, moptions, filePath, clientId);
+					if (ret == TCErrorConstants.TCAPI_ERR_NONE) {
+						this.cookie.setClientId(clientId[0]);
+						this.cookie.setConnected(true);
+						this.connection = inConnection;
+						this.messageOptions = inMessageOptions;
+					} else {
+						status = new Status(Status.ERROR, Activator.PLUGIN_ID, (int)ret, TCErrorConstants.getErrorMessage(ret), null);
+					}
+				} else {
+					status = new Status(Status.ERROR, Activator.PLUGIN_ID, (int)ret, TCErrorConstants.getErrorMessage(ret), null);
+				}
 			} else {
 				status = new Status(Status.ERROR, Activator.PLUGIN_ID, (int)ret, TCErrorConstants.getErrorMessage(ret), null);
 			}