1/*-------------------------------------------------------------------------
2 * drawElements Quality Program Execution Server
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 TestProcess implementation for Win32.
22 *//*--------------------------------------------------------------------*/
23
24#include "xsWin32TestProcess.hpp"
25#include "deFilePath.hpp"
26#include "deString.h"
27#include "deMemory.h"
28#include "deClock.h"
29#include "deFile.h"
30
31#include <sstream>
32#include <string.h>
33
34using std::string;
35using std::vector;
36
37namespace xs
38{
39
40enum
41{
42	MAX_OLD_LOGFILE_DELETE_ATTEMPTS		= 20,	//!< How many times execserver tries to delete old log file
43	LOGFILE_DELETE_SLEEP_MS				= 50	//!< Sleep time (in ms) between log file delete attempts
44};
45
46namespace win32
47{
48
49// Error
50
51static std::string formatErrMsg (DWORD error, const char* msg)
52{
53	std::ostringstream	str;
54	LPSTR				msgBuf;
55
56#if defined(UNICODE)
57#	error Unicode not supported.
58#endif
59
60	if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
61					  NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&msgBuf, 0, DE_NULL) > 0)
62		str << msg << ", error " << error << ": " << msgBuf;
63	else
64		str << msg << ", error " << error;
65
66	return str.str();
67}
68
69Error::Error (DWORD error, const char* msg)
70	: std::runtime_error(formatErrMsg(error, msg))
71	, m_error			(error)
72{
73}
74
75// Event
76
77Event::Event (bool manualReset, bool initialState)
78	: m_handle(0)
79{
80	m_handle = CreateEvent(NULL, manualReset ? TRUE : FALSE, initialState ? TRUE : FALSE, NULL);
81	if (!m_handle)
82		throw Error(GetLastError(), "CreateEvent() failed");
83}
84
85Event::~Event (void)
86{
87	CloseHandle(m_handle);
88}
89
90void Event::setSignaled (void)
91{
92	if (!SetEvent(m_handle))
93		throw Error(GetLastError(), "SetEvent() failed");
94}
95
96void Event::reset (void)
97{
98	if (!ResetEvent(m_handle))
99		throw Error(GetLastError(), "ResetEvent() failed");
100}
101
102// CaseListWriter
103
104CaseListWriter::CaseListWriter (void)
105	: m_dst			(INVALID_HANDLE_VALUE)
106	, m_cancelEvent	(true, false)
107{
108}
109
110CaseListWriter::~CaseListWriter (void)
111{
112}
113
114void CaseListWriter::start (const char* caseList, HANDLE dst)
115{
116	DE_ASSERT(!isStarted());
117
118	m_dst = dst;
119
120	int caseListSize = (int)strlen(caseList)+1;
121	m_caseList.resize(caseListSize);
122	std::copy(caseList, caseList+caseListSize, m_caseList.begin());
123
124	de::Thread::start();
125}
126
127void CaseListWriter::run (void)
128{
129	try
130	{
131		Event		ioEvent			(true, false); // Manual reset, non-signaled state.
132		HANDLE		waitHandles[]	= { ioEvent.getHandle(), m_cancelEvent.getHandle() };
133		OVERLAPPED	overlapped;
134		int			curPos = 0;
135
136		deMemset(&overlapped, 0, sizeof(overlapped));
137		overlapped.hEvent = ioEvent.getHandle();
138
139		while (curPos < (int)m_caseList.size())
140		{
141			const int	maxWriteSize	= 4096;
142			const int	numToWrite		= de::min(maxWriteSize, (int)m_caseList.size() - curPos);
143			DWORD		waitRes			= 0;
144
145			if (!WriteFile(m_dst, &m_caseList[curPos], (DWORD)numToWrite, NULL, &overlapped))
146			{
147				DWORD err = GetLastError();
148				if (err != ERROR_IO_PENDING)
149					throw Error(err, "WriteFile() failed");
150			}
151
152			waitRes = WaitForMultipleObjects(DE_LENGTH_OF_ARRAY(waitHandles), &waitHandles[0], FALSE, INFINITE);
153
154			if (waitRes == WAIT_OBJECT_0)
155			{
156				DWORD numBytesWritten = 0;
157
158				// \note GetOverlappedResult() will fail with ERROR_IO_INCOMPLETE if IO event is not complete (should be).
159				if (!GetOverlappedResult(m_dst, &overlapped, &numBytesWritten, FALSE))
160					throw Error(GetLastError(), "GetOverlappedResult() failed");
161
162				if (numBytesWritten == 0)
163					throw Error(GetLastError(), "Writing to pipe failed (pipe closed?)");
164
165				curPos += (int)numBytesWritten;
166			}
167			else if (waitRes == WAIT_OBJECT_0 + 1)
168			{
169				// Cancel.
170				if (!CancelIo(m_dst))
171					throw Error(GetLastError(), "CancelIo() failed");
172				break;
173			}
174			else
175				throw Error(GetLastError(), "WaitForMultipleObjects() failed");
176		}
177	}
178	catch (const std::exception& e)
179	{
180		// \todo [2013-08-13 pyry] What to do about this?
181		printf("win32::CaseListWriter::run(): %s\n", e.what());
182	}
183}
184
185void CaseListWriter::stop (void)
186{
187	if (!isStarted())
188		return; // Nothing to do.
189
190	m_cancelEvent.setSignaled();
191
192	// Join thread.
193	join();
194
195	m_cancelEvent.reset();
196
197	m_dst = INVALID_HANDLE_VALUE;
198}
199
200// FileReader
201
202FileReader::FileReader (ThreadedByteBuffer* dst)
203	: m_dstBuf		(dst)
204	, m_handle		(INVALID_HANDLE_VALUE)
205	, m_cancelEvent	(false, false)
206{
207}
208
209FileReader::~FileReader (void)
210{
211}
212
213void FileReader::start (HANDLE file)
214{
215	DE_ASSERT(!isStarted());
216
217	m_handle = file;
218
219	de::Thread::start();
220}
221
222void FileReader::run (void)
223{
224	try
225	{
226		Event					ioEvent			(true, false); // Manual reset, not signaled state.
227		HANDLE					waitHandles[]	= { ioEvent.getHandle(), m_cancelEvent.getHandle() };
228		OVERLAPPED				overlapped;
229		std::vector<deUint8>	tmpBuf			(FILEREADER_TMP_BUFFER_SIZE);
230		deUint64				offset			= 0; // Overlapped IO requires manual offset keeping.
231
232		deMemset(&overlapped, 0, sizeof(overlapped));
233		overlapped.hEvent = ioEvent.getHandle();
234
235		for (;;)
236		{
237			DWORD	numBytesRead	= 0;
238			DWORD	waitRes;
239
240			overlapped.Offset		= (DWORD)(offset & 0xffffffffu);
241			overlapped.OffsetHigh	= (DWORD)(offset >> 32);
242
243			if (!ReadFile(m_handle, &tmpBuf[0], (DWORD)tmpBuf.size(), NULL, &overlapped))
244			{
245				DWORD err = GetLastError();
246
247				if (err == ERROR_BROKEN_PIPE)
248					break;
249				else if (err == ERROR_HANDLE_EOF)
250				{
251					if (m_dstBuf->isCanceled())
252						break;
253
254					deSleep(FILEREADER_IDLE_SLEEP);
255
256					if (m_dstBuf->isCanceled())
257						break;
258					else
259						continue;
260				}
261				else if (err != ERROR_IO_PENDING)
262					throw Error(err, "ReadFile() failed");
263			}
264
265			waitRes = WaitForMultipleObjects(DE_LENGTH_OF_ARRAY(waitHandles), &waitHandles[0], FALSE, INFINITE);
266
267			if (waitRes == WAIT_OBJECT_0)
268			{
269				// \note GetOverlappedResult() will fail with ERROR_IO_INCOMPLETE if IO event is not complete (should be).
270				if (!GetOverlappedResult(m_handle, &overlapped, &numBytesRead, FALSE))
271				{
272					DWORD err = GetLastError();
273
274					if (err == ERROR_HANDLE_EOF)
275					{
276						// End of file - for now.
277						// \note Should check for end of buffer here, or otherwise may end up in infinite loop.
278						if (m_dstBuf->isCanceled())
279							break;
280
281						deSleep(FILEREADER_IDLE_SLEEP);
282
283						if (m_dstBuf->isCanceled())
284							break;
285						else
286							continue;
287					}
288					else if (err == ERROR_BROKEN_PIPE)
289						break;
290					else
291						throw Error(err, "GetOverlappedResult() failed");
292				}
293
294				if (numBytesRead == 0)
295					throw Error(GetLastError(), "Reading from file failed");
296				else
297					offset += (deUint64)numBytesRead;
298			}
299			else if (waitRes == WAIT_OBJECT_0 + 1)
300			{
301				// Cancel.
302				if (!CancelIo(m_handle))
303					throw Error(GetLastError(), "CancelIo() failed");
304				break;
305			}
306			else
307				throw Error(GetLastError(), "WaitForMultipleObjects() failed");
308
309			try
310			{
311				m_dstBuf->write((int)numBytesRead, &tmpBuf[0]);
312				m_dstBuf->flush();
313			}
314			catch (const ThreadedByteBuffer::CanceledException&)
315			{
316				// Canceled.
317				break;
318			}
319		}
320	}
321	catch (const std::exception& e)
322	{
323		// \todo [2013-08-13 pyry] What to do?
324		printf("win32::FileReader::run(): %s\n", e.what());
325	}
326}
327
328void FileReader::stop (void)
329{
330	if (!isStarted())
331		return; // Nothing to do.
332
333	m_cancelEvent.setSignaled();
334
335	// Join thread.
336	join();
337
338	m_cancelEvent.reset();
339
340	m_handle = INVALID_HANDLE_VALUE;
341}
342
343// TestLogReader
344
345TestLogReader::TestLogReader (void)
346	: m_logBuffer	(LOG_BUFFER_BLOCK_SIZE, LOG_BUFFER_NUM_BLOCKS)
347	, m_logFile		(INVALID_HANDLE_VALUE)
348	, m_reader		(&m_logBuffer)
349{
350}
351
352TestLogReader::~TestLogReader (void)
353{
354	if (m_logFile != INVALID_HANDLE_VALUE)
355		CloseHandle(m_logFile);
356}
357
358void TestLogReader::start (const char* filename)
359{
360	DE_ASSERT(m_logFile == INVALID_HANDLE_VALUE && !m_reader.isStarted());
361
362	m_logFile = CreateFile(filename,
363						   GENERIC_READ,
364						   FILE_SHARE_DELETE|FILE_SHARE_READ|FILE_SHARE_WRITE,
365						   DE_NULL,
366						   OPEN_EXISTING,
367						   FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,
368						   DE_NULL);
369
370	if (m_logFile == INVALID_HANDLE_VALUE)
371		throw Error(GetLastError(), "Failed to open log file");
372
373	m_reader.start(m_logFile);
374}
375
376void TestLogReader::stop (void)
377{
378	if (!m_reader.isStarted())
379		return; // Nothing to do.
380
381	m_logBuffer.cancel();
382	m_reader.stop();
383
384	CloseHandle(m_logFile);
385	m_logFile = INVALID_HANDLE_VALUE;
386
387	m_logBuffer.clear();
388}
389
390// Process
391
392Process::Process (void)
393	: m_state		(STATE_NOT_STARTED)
394	, m_exitCode	(0)
395	, m_standardIn	(INVALID_HANDLE_VALUE)
396	, m_standardOut	(INVALID_HANDLE_VALUE)
397	, m_standardErr	(INVALID_HANDLE_VALUE)
398{
399	deMemset(&m_procInfo, 0, sizeof(m_procInfo));
400}
401
402Process::~Process (void)
403{
404	try
405	{
406		if (isRunning())
407		{
408			kill();
409			waitForFinish();
410		}
411	}
412	catch (...)
413	{
414	}
415
416	cleanupHandles();
417}
418
419void Process::cleanupHandles (void)
420{
421	DE_ASSERT(!isRunning());
422
423	if (m_standardErr != INVALID_HANDLE_VALUE)
424		CloseHandle(m_standardErr);
425
426	if (m_standardOut != INVALID_HANDLE_VALUE)
427		CloseHandle(m_standardOut);
428
429	if (m_standardIn != INVALID_HANDLE_VALUE)
430		CloseHandle(m_standardIn);
431
432	if (m_procInfo.hProcess)
433		CloseHandle(m_procInfo.hProcess);
434
435	if (m_procInfo.hThread)
436		CloseHandle(m_procInfo.hThread);
437
438	m_standardErr	= INVALID_HANDLE_VALUE;
439	m_standardOut	= INVALID_HANDLE_VALUE;
440	m_standardIn	= INVALID_HANDLE_VALUE;
441
442	deMemset(&m_procInfo, 0, sizeof(m_procInfo));
443}
444
445__declspec(thread) static int t_pipeNdx = 0;
446
447static void createPipeWithOverlappedIO (HANDLE* readHandleOut, HANDLE* writeHandleOut, deUint32 readMode, deUint32 writeMode, SECURITY_ATTRIBUTES* securityAttr)
448{
449	const int	defaultBufSize	= 4096;
450	char		pipeName[128];
451	HANDLE		readHandle;
452	HANDLE		writeHandle;
453
454	DE_ASSERT(((readMode | writeMode) & ~FILE_FLAG_OVERLAPPED) == 0);
455
456	deSprintf(pipeName, sizeof(pipeName), "\\\\.\\Pipe\\dEQP-ExecServer-%08x-%08x-%08x",
457			  GetCurrentProcessId(),
458			  GetCurrentThreadId(),
459			  t_pipeNdx++);
460
461	readHandle = CreateNamedPipe(pipeName,						/* Pipe name.				*/
462								 PIPE_ACCESS_INBOUND|readMode,	/* Open mode.				*/
463								 PIPE_TYPE_BYTE|PIPE_WAIT,		/* Pipe flags.				*/
464								 1,								/* Max number of instances.	*/
465								 defaultBufSize,				/* Output buffer size.		*/
466								 defaultBufSize,				/* Input buffer size.		*/
467								 0,								/* Use default timeout.		*/
468								 securityAttr);
469
470	if (readHandle == INVALID_HANDLE_VALUE)
471		throw Error(GetLastError(), "CreateNamedPipe() failed");
472
473	writeHandle = CreateFile(pipeName,
474							 GENERIC_WRITE,						/* Access mode.				*/
475							 0,									/* No sharing.				*/
476							 securityAttr,
477							 OPEN_EXISTING,						/* Assume existing object.	*/
478							 FILE_ATTRIBUTE_NORMAL|writeMode,	/* Open mode / flags.		*/
479							 DE_NULL							/* Template file.			*/);
480
481	if (writeHandle == INVALID_HANDLE_VALUE)
482	{
483		DWORD openErr = GetLastError();
484		CloseHandle(readHandle);
485		throw Error(openErr, "Failed to open created pipe, CreateFile() failed");
486	}
487
488	*readHandleOut	= readHandle;
489	*writeHandleOut	= writeHandle;
490}
491
492void Process::start (const char* commandLine, const char* workingDirectory)
493{
494	// Pipes.
495	HANDLE		stdInRead	= INVALID_HANDLE_VALUE;
496	HANDLE		stdInWrite	= INVALID_HANDLE_VALUE;
497	HANDLE		stdOutRead	= INVALID_HANDLE_VALUE;
498	HANDLE		stdOutWrite	= INVALID_HANDLE_VALUE;
499	HANDLE		stdErrRead	= INVALID_HANDLE_VALUE;
500	HANDLE		stdErrWrite	= INVALID_HANDLE_VALUE;
501
502	if (m_state == STATE_RUNNING)
503		throw std::runtime_error("Process already running");
504	else if (m_state == STATE_FINISHED)
505	{
506		// Process finished, clean up old cruft.
507		cleanupHandles();
508		m_state = STATE_NOT_STARTED;
509	}
510
511	// Create pipes
512	try
513	{
514		SECURITY_ATTRIBUTES	securityAttr;
515		STARTUPINFO			startInfo;
516
517		deMemset(&startInfo, 0, sizeof(startInfo));
518		deMemset(&securityAttr, 0, sizeof(securityAttr));
519
520		// Security attributes for inheriting handle.
521		securityAttr.nLength				= sizeof(SECURITY_ATTRIBUTES);
522		securityAttr.bInheritHandle			= TRUE;
523		securityAttr.lpSecurityDescriptor	= DE_NULL;
524
525		createPipeWithOverlappedIO(&stdInRead,	&stdInWrite,	0, FILE_FLAG_OVERLAPPED, &securityAttr);
526		createPipeWithOverlappedIO(&stdOutRead,	&stdOutWrite,	FILE_FLAG_OVERLAPPED, 0, &securityAttr);
527		createPipeWithOverlappedIO(&stdErrRead,	&stdErrWrite,	FILE_FLAG_OVERLAPPED, 0, &securityAttr);
528
529		if (!SetHandleInformation(stdInWrite, HANDLE_FLAG_INHERIT, 0) ||
530			!SetHandleInformation(stdOutRead, HANDLE_FLAG_INHERIT, 0) ||
531			!SetHandleInformation(stdErrRead, HANDLE_FLAG_INHERIT, 0))
532			throw Error(GetLastError(), "SetHandleInformation() failed");
533
534		// Startup info for process.
535		startInfo.cb			= sizeof(startInfo);
536		startInfo.hStdError		 = stdErrWrite;
537		startInfo.hStdOutput	 = stdOutWrite;
538		startInfo.hStdInput		 = stdInRead;
539		startInfo.dwFlags		|= STARTF_USESTDHANDLES;
540
541		if (!CreateProcess(DE_NULL, (LPTSTR)commandLine, DE_NULL, DE_NULL, TRUE /* inherit handles */, 0, DE_NULL, workingDirectory, &startInfo, &m_procInfo))
542			throw Error(GetLastError(), "CreateProcess() failed");
543	}
544	catch (...)
545	{
546		if (stdInRead	!= INVALID_HANDLE_VALUE)	CloseHandle(stdInRead);
547		if (stdInWrite	!= INVALID_HANDLE_VALUE)	CloseHandle(stdInWrite);
548		if (stdOutRead	!= INVALID_HANDLE_VALUE)	CloseHandle(stdOutRead);
549		if (stdOutWrite	!= INVALID_HANDLE_VALUE)	CloseHandle(stdOutWrite);
550		if (stdErrRead	!= INVALID_HANDLE_VALUE)	CloseHandle(stdErrRead);
551		if (stdErrWrite	!= INVALID_HANDLE_VALUE)	CloseHandle(stdErrWrite);
552		throw;
553	}
554
555	// Store handles to be kept.
556	m_standardIn	= stdInWrite;
557	m_standardOut	= stdOutRead;
558	m_standardErr	= stdErrRead;
559
560	// Close other ends of handles.
561	CloseHandle(stdErrWrite);
562	CloseHandle(stdOutWrite);
563	CloseHandle(stdInRead);
564
565	m_state = STATE_RUNNING;
566}
567
568bool Process::isRunning (void)
569{
570	if (m_state == STATE_RUNNING)
571	{
572		int exitCode;
573		BOOL result = GetExitCodeProcess(m_procInfo.hProcess, (LPDWORD)&exitCode);
574
575		if (result != TRUE)
576			throw Error(GetLastError(), "GetExitCodeProcess() failed");
577
578		if (exitCode == STILL_ACTIVE)
579			return true;
580		else
581		{
582			// Done.
583			m_exitCode	= exitCode;
584			m_state		= STATE_FINISHED;
585			return false;
586		}
587	}
588	else
589		return false;
590}
591
592void Process::waitForFinish (void)
593{
594	if (m_state == STATE_RUNNING)
595	{
596		if (WaitForSingleObject(m_procInfo.hProcess, INFINITE) != WAIT_OBJECT_0)
597			throw Error(GetLastError(), "Waiting for process failed, WaitForSingleObject() failed");
598
599		if (isRunning())
600			throw std::runtime_error("Process is still alive");
601	}
602	else
603		throw std::runtime_error("Process is not running");
604}
605
606void Process::stopProcess (bool kill)
607{
608	if (m_state == STATE_RUNNING)
609	{
610		if (!TerminateProcess(m_procInfo.hProcess, kill ? -1 : 0))
611			throw Error(GetLastError(), "TerminateProcess() failed");
612	}
613	else
614		throw std::runtime_error("Process is not running");
615}
616
617void Process::terminate (void)
618{
619	stopProcess(false);
620}
621
622void Process::kill (void)
623{
624	stopProcess(true);
625}
626
627} // win32
628
629Win32TestProcess::Win32TestProcess (void)
630	: m_process				(DE_NULL)
631	, m_processStartTime	(0)
632	, m_infoBuffer			(INFO_BUFFER_BLOCK_SIZE, INFO_BUFFER_NUM_BLOCKS)
633	, m_stdOutReader		(&m_infoBuffer)
634	, m_stdErrReader		(&m_infoBuffer)
635{
636}
637
638Win32TestProcess::~Win32TestProcess (void)
639{
640	delete m_process;
641}
642
643void Win32TestProcess::start (const char* name, const char* params, const char* workingDir, const char* caseList)
644{
645	bool hasCaseList = strlen(caseList) > 0;
646
647	XS_CHECK(!m_process);
648
649	de::FilePath logFilePath = de::FilePath::join(workingDir, "TestResults.qpa");
650	m_logFileName = logFilePath.getPath();
651
652	// Remove old file if such exists.
653	// \note Sometimes on Windows the test process dies slowly and may not release handle to log file
654	//		 until a bit later.
655	// \todo [2013-07-15 pyry] This should be solved by improving deProcess and killing all child processes as well.
656	{
657		int tryNdx = 0;
658		while (tryNdx < MAX_OLD_LOGFILE_DELETE_ATTEMPTS && deFileExists(m_logFileName.c_str()))
659		{
660			if (deDeleteFile(m_logFileName.c_str()))
661				break;
662			deSleep(LOGFILE_DELETE_SLEEP_MS);
663			tryNdx += 1;
664		}
665
666		if (deFileExists(m_logFileName.c_str()))
667			throw TestProcessException(string("Failed to remove '") + m_logFileName + "'");
668	}
669
670	// Construct command line.
671	string cmdLine = de::FilePath(name).isAbsolutePath() ? name : de::FilePath::join(workingDir, name).normalize().getPath();
672	cmdLine += string(" --deqp-log-filename=") + logFilePath.getBaseName();
673
674	if (hasCaseList)
675		cmdLine += " --deqp-stdin-caselist";
676
677	if (strlen(params) > 0)
678		cmdLine += string(" ") + params;
679
680	DE_ASSERT(!m_process);
681	m_process = new win32::Process();
682
683	try
684	{
685		m_process->start(cmdLine.c_str(), strlen(workingDir) > 0 ? workingDir : DE_NULL);
686	}
687	catch (const std::exception& e)
688	{
689		delete m_process;
690		m_process = DE_NULL;
691		throw TestProcessException(e.what());
692	}
693
694	m_processStartTime = deGetMicroseconds();
695
696	// Create stdout & stderr readers.
697	m_stdOutReader.start(m_process->getStdOut());
698	m_stdErrReader.start(m_process->getStdErr());
699
700	// Start case list writer.
701	if (hasCaseList)
702		m_caseListWriter.start(caseList, m_process->getStdIn());
703}
704
705void Win32TestProcess::terminate (void)
706{
707	if (m_process)
708	{
709		try
710		{
711			m_process->kill();
712		}
713		catch (const std::exception& e)
714		{
715			printf("Win32TestProcess::terminate(): Failed to kill process: %s\n", e.what());
716		}
717	}
718}
719
720void Win32TestProcess::cleanup (void)
721{
722	m_caseListWriter.stop();
723
724	// \note Buffers must be canceled before stopping readers.
725	m_infoBuffer.cancel();
726
727	m_stdErrReader.stop();
728	m_stdOutReader.stop();
729	m_testLogReader.stop();
730
731	// Reset buffers.
732	m_infoBuffer.clear();
733
734	if (m_process)
735	{
736		try
737		{
738			if (m_process->isRunning())
739			{
740				m_process->kill();
741				m_process->waitForFinish();
742			}
743		}
744		catch (const std::exception& e)
745		{
746			printf("Win32TestProcess::cleanup(): Failed to kill process: %s\n", e.what());
747		}
748
749		delete m_process;
750		m_process = DE_NULL;
751	}
752}
753
754int Win32TestProcess::readTestLog (deUint8* dst, int numBytes)
755{
756	if (!m_testLogReader.isRunning())
757	{
758		if (deGetMicroseconds() - m_processStartTime > LOG_FILE_TIMEOUT*1000)
759		{
760			// Timeout, kill process.
761			terminate();
762			return 0; // \todo [2013-08-13 pyry] Throw exception?
763		}
764
765		if (!deFileExists(m_logFileName.c_str()))
766			return 0;
767
768		// Start reader.
769		m_testLogReader.start(m_logFileName.c_str());
770	}
771
772	DE_ASSERT(m_testLogReader.isRunning());
773	return m_testLogReader.read(dst, numBytes);
774}
775
776bool Win32TestProcess::isRunning (void)
777{
778	if (m_process)
779		return m_process->isRunning();
780	else
781		return false;
782}
783
784int Win32TestProcess::getExitCode (void) const
785{
786	if (m_process)
787		return m_process->getExitCode();
788	else
789		return -1;
790}
791
792} // xs
793