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