1 /*-------------------------------------------------------------------------
2  * drawElements Quality Program Test Executor
3  * ------------------------------------------
4  *
5  * Copyright 2014 The Android Open Source Project
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  *//*!
20  * \file
21  * \brief Tcp/Ip link that manages execserver process.
22  *//*--------------------------------------------------------------------*/
23 
24 #include "xeLocalTcpIpLink.hpp"
25 #include "deClock.h"
26 #include "deThread.h"
27 
28 #include <sstream>
29 
30 enum
31 {
32 	SERVER_START_TIMEOUT	= 1000,
33 	SERVER_START_IDLE_SLEEP	= 50
34 };
35 
36 namespace xe
37 {
38 
LocalTcpIpLink(void)39 LocalTcpIpLink::LocalTcpIpLink (void)
40 	: m_process(DE_NULL)
41 {
42 }
43 
~LocalTcpIpLink(void)44 LocalTcpIpLink::~LocalTcpIpLink (void)
45 {
46 	stop();
47 }
48 
start(const char * execServerPath,const char * workDir,int port)49 void LocalTcpIpLink::start (const char* execServerPath, const char* workDir, int port)
50 {
51 	XE_CHECK(!m_process);
52 
53 	std::ostringstream cmdLine;
54 	cmdLine << execServerPath << " --single --port=" << port;
55 
56 	m_process = deProcess_create();
57 	XE_CHECK(m_process);
58 
59 	if (deProcess_start(m_process, cmdLine.str().c_str(), workDir) != DE_TRUE)
60 	{
61 		std::string err = deProcess_getLastError(m_process);
62 		deProcess_destroy(m_process);
63 		m_process = DE_NULL;
64 
65 		XE_FAIL((std::string("Failed to start ExecServer '") + execServerPath + "' : " + err).c_str());
66 	}
67 
68 	try
69 	{
70 		de::SocketAddress address;
71 		address.setFamily	(DE_SOCKETFAMILY_INET4);
72 		address.setProtocol	(DE_SOCKETPROTOCOL_TCP);
73 		address.setHost		("127.0.0.1");
74 		address.setPort		(port);
75 
76 		// Wait until server has started - \todo [2012-07-19 pyry] This could be improved by having server to signal when it is ready.
77 		deUint64 waitStart = deGetMicroseconds();
78 		for (;;)
79 		{
80 			if (!deProcess_isRunning(m_process))
81 				XE_FAIL("ExecServer died");
82 
83 			try
84 			{
85 				m_link.connect(address);
86 				break;
87 			}
88 			catch (const de::SocketError&)
89 			{
90 				if (deGetMicroseconds()-waitStart > SERVER_START_TIMEOUT*1000)
91 					XE_FAIL("Server start timeout");
92 
93 				deSleep(SERVER_START_IDLE_SLEEP);
94 			}
95 		}
96 
97 		// Close stdout/stderr or otherwise process will hang once OS pipe buffers are full.
98 		// \todo [2012-07-19 pyry] Read and store stdout/stderr from execserver.
99 		XE_CHECK(deProcess_closeStdOut(m_process));
100 		XE_CHECK(deProcess_closeStdErr(m_process));
101 	}
102 	catch (const std::exception&)
103 	{
104 		stop();
105 		throw;
106 	}
107 }
108 
stop(void)109 void LocalTcpIpLink::stop (void)
110 {
111 	if (m_process)
112 	{
113 		try
114 		{
115 			m_link.disconnect();
116 		}
117 		catch (...)
118 		{
119 			// Silently ignore since this is called in destructor.
120 		}
121 
122 		// \note --single flag is used so execserver should kill itself once one connection is handled.
123 		//		 This is here to make sure it dies even in case of hang.
124 		deProcess_terminate		(m_process);
125 		deProcess_waitForFinish	(m_process);
126 		deProcess_destroy		(m_process);
127 
128 		m_process = DE_NULL;
129 	}
130 }
131 
reset(void)132 void LocalTcpIpLink::reset (void)
133 {
134 	m_link.reset();
135 }
136 
getState(void) const137 CommLinkState LocalTcpIpLink::getState (void) const
138 {
139 	if (!m_process)
140 		return COMMLINKSTATE_ERROR;
141 	else
142 		return m_link.getState();
143 }
144 
getState(std::string & error) const145 CommLinkState LocalTcpIpLink::getState (std::string& error) const
146 {
147 	if (!m_process)
148 	{
149 		error = "Not started";
150 		return COMMLINKSTATE_ERROR;
151 	}
152 	else
153 		return m_link.getState();
154 }
155 
setCallbacks(StateChangedFunc stateChangedCallback,LogDataFunc testLogDataCallback,LogDataFunc infoLogDataCallback,void * userPtr)156 void LocalTcpIpLink::setCallbacks (StateChangedFunc stateChangedCallback, LogDataFunc testLogDataCallback, LogDataFunc infoLogDataCallback, void* userPtr)
157 {
158 	m_link.setCallbacks(stateChangedCallback, testLogDataCallback, infoLogDataCallback, userPtr);
159 }
160 
startTestProcess(const char * name,const char * params,const char * workingDir,const char * caseList)161 void LocalTcpIpLink::startTestProcess (const char* name, const char* params, const char* workingDir, const char* caseList)
162 {
163 	if (m_process)
164 		m_link.startTestProcess(name, params, workingDir, caseList);
165 	else
166 		XE_FAIL("Not started");
167 }
168 
stopTestProcess(void)169 void LocalTcpIpLink::stopTestProcess (void)
170 {
171 	if (m_process)
172 		m_link.stopTestProcess();
173 	else
174 		XE_FAIL("Not started");
175 }
176 
177 } // xe
178