/*-------------------------------------------------------------------------
* drawElements Quality Program Test Executor
* ------------------------------------------
*
* Copyright 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*//*!
* \file
* \brief Tcp/Ip link that manages execserver process.
*//*--------------------------------------------------------------------*/
#include "xeLocalTcpIpLink.hpp"
#include "deClock.h"
#include "deThread.h"
#include <sstream>
enum
{
SERVER_START_TIMEOUT = 1000,
SERVER_START_IDLE_SLEEP = 50
};
namespace xe
{
LocalTcpIpLink::LocalTcpIpLink (void)
: m_process(DE_NULL)
{
}
LocalTcpIpLink::~LocalTcpIpLink (void)
{
stop();
}
void LocalTcpIpLink::start (const char* execServerPath, const char* workDir, int port)
{
XE_CHECK(!m_process);
std::ostringstream cmdLine;
cmdLine << execServerPath << " --single --port=" << port;
m_process = deProcess_create();
XE_CHECK(m_process);
if (deProcess_start(m_process, cmdLine.str().c_str(), workDir) != DE_TRUE)
{
std::string err = deProcess_getLastError(m_process);
deProcess_destroy(m_process);
m_process = DE_NULL;
XE_FAIL((std::string("Failed to start ExecServer '") + execServerPath + "' : " + err).c_str());
}
try
{
de::SocketAddress address;
address.setFamily (DE_SOCKETFAMILY_INET4);
address.setProtocol (DE_SOCKETPROTOCOL_TCP);
address.setHost ("127.0.0.1");
address.setPort (port);
// Wait until server has started - \todo [2012-07-19 pyry] This could be improved by having server to signal when it is ready.
deUint64 waitStart = deGetMicroseconds();
for (;;)
{
if (!deProcess_isRunning(m_process))
XE_FAIL("ExecServer died");
try
{
m_link.connect(address);
break;
}
catch (const de::SocketError&)
{
if (deGetMicroseconds()-waitStart > SERVER_START_TIMEOUT*1000)
XE_FAIL("Server start timeout");
deSleep(SERVER_START_IDLE_SLEEP);
}
}
// Close stdout/stderr or otherwise process will hang once OS pipe buffers are full.
// \todo [2012-07-19 pyry] Read and store stdout/stderr from execserver.
XE_CHECK(deProcess_closeStdOut(m_process));
XE_CHECK(deProcess_closeStdErr(m_process));
}
catch (const std::exception&)
{
stop();
throw;
}
}
void LocalTcpIpLink::stop (void)
{
if (m_process)
{
try
{
m_link.disconnect();
}
catch (...)
{
// Silently ignore since this is called in destructor.
}
// \note --single flag is used so execserver should kill itself once one connection is handled.
// This is here to make sure it dies even in case of hang.
deProcess_terminate (m_process);
deProcess_waitForFinish (m_process);
deProcess_destroy (m_process);
m_process = DE_NULL;
}
}
void LocalTcpIpLink::reset (void)
{
m_link.reset();
}
CommLinkState LocalTcpIpLink::getState (void) const
{
if (!m_process)
return COMMLINKSTATE_ERROR;
else
return m_link.getState();
}
CommLinkState LocalTcpIpLink::getState (std::string& error) const
{
if (!m_process)
{
error = "Not started";
return COMMLINKSTATE_ERROR;
}
else
return m_link.getState();
}
void LocalTcpIpLink::setCallbacks (StateChangedFunc stateChangedCallback, LogDataFunc testLogDataCallback, LogDataFunc infoLogDataCallback, void* userPtr)
{
m_link.setCallbacks(stateChangedCallback, testLogDataCallback, infoLogDataCallback, userPtr);
}
void LocalTcpIpLink::startTestProcess (const char* name, const char* params, const char* workingDir, const char* caseList)
{
if (m_process)
m_link.startTestProcess(name, params, workingDir, caseList);
else
XE_FAIL("Not started");
}
void LocalTcpIpLink::stopTestProcess (void)
{
if (m_process)
m_link.stopTestProcess();
else
XE_FAIL("Not started");
}
} // xe