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
34 using std::string;
35 using std::vector;
36
37 namespace xs
38 {
39
40 enum
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
46 namespace win32
47 {
48
49 // Error
50
formatErrMsg(DWORD error,const char * msg)51 static 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
Error(DWORD error,const char * msg)69 Error::Error (DWORD error, const char* msg)
70 : std::runtime_error(formatErrMsg(error, msg))
71 , m_error (error)
72 {
73 }
74
75 // Event
76
Event(bool manualReset,bool initialState)77 Event::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
~Event(void)85 Event::~Event (void)
86 {
87 CloseHandle(m_handle);
88 }
89
setSignaled(void)90 void Event::setSignaled (void)
91 {
92 if (!SetEvent(m_handle))
93 throw Error(GetLastError(), "SetEvent() failed");
94 }
95
reset(void)96 void Event::reset (void)
97 {
98 if (!ResetEvent(m_handle))
99 throw Error(GetLastError(), "ResetEvent() failed");
100 }
101
102 // CaseListWriter
103
CaseListWriter(void)104 CaseListWriter::CaseListWriter (void)
105 : m_dst (INVALID_HANDLE_VALUE)
106 , m_cancelEvent (true, false)
107 {
108 }
109
~CaseListWriter(void)110 CaseListWriter::~CaseListWriter (void)
111 {
112 }
113
start(const char * caseList,HANDLE dst)114 void 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
run(void)127 void 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
stop(void)185 void 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
FileReader(ThreadedByteBuffer * dst)202 FileReader::FileReader (ThreadedByteBuffer* dst)
203 : m_dstBuf (dst)
204 , m_handle (INVALID_HANDLE_VALUE)
205 , m_cancelEvent (false, false)
206 {
207 }
208
~FileReader(void)209 FileReader::~FileReader (void)
210 {
211 }
212
start(HANDLE file)213 void FileReader::start (HANDLE file)
214 {
215 DE_ASSERT(!isStarted());
216
217 m_handle = file;
218
219 de::Thread::start();
220 }
221
run(void)222 void 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
stop(void)328 void 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
TestLogReader(void)345 TestLogReader::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
~TestLogReader(void)352 TestLogReader::~TestLogReader (void)
353 {
354 if (m_logFile != INVALID_HANDLE_VALUE)
355 CloseHandle(m_logFile);
356 }
357
start(const char * filename)358 void 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
stop(void)376 void 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
Process(void)392 Process::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
~Process(void)402 Process::~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
cleanupHandles(void)419 void 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
createPipeWithOverlappedIO(HANDLE * readHandleOut,HANDLE * writeHandleOut,deUint32 readMode,deUint32 writeMode,SECURITY_ATTRIBUTES * securityAttr)447 static 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
start(const char * commandLine,const char * workingDirectory)492 void 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
isRunning(void)568 bool 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
waitForFinish(void)592 void 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
stopProcess(bool kill)606 void 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
terminate(void)617 void Process::terminate (void)
618 {
619 stopProcess(false);
620 }
621
kill(void)622 void Process::kill (void)
623 {
624 stopProcess(true);
625 }
626
627 } // win32
628
Win32TestProcess(void)629 Win32TestProcess::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
~Win32TestProcess(void)638 Win32TestProcess::~Win32TestProcess (void)
639 {
640 delete m_process;
641 }
642
start(const char * name,const char * params,const char * workingDir,const char * caseList)643 void 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
terminate(void)705 void 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
cleanup(void)720 void 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
readTestLog(deUint8 * dst,int numBytes)754 int 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
isRunning(void)776 bool Win32TestProcess::isRunning (void)
777 {
778 if (m_process)
779 return m_process->isRunning();
780 else
781 return false;
782 }
783
getExitCode(void) const784 int Win32TestProcess::getExitCode (void) const
785 {
786 if (m_process)
787 return m_process->getExitCode();
788 else
789 return -1;
790 }
791
792 } // xs
793