Merge branch '361-memory-properties' into 'vulkan-cts-1.0'
[platform/upstream/VK-GL-CTS.git] / execserver / xsWin32TestProcess.cpp
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
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
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
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
85 Event::~Event (void)
86 {
87         CloseHandle(m_handle);
88 }
89
90 void Event::setSignaled (void)
91 {
92         if (!SetEvent(m_handle))
93                 throw Error(GetLastError(), "SetEvent() failed");
94 }
95
96 void Event::reset (void)
97 {
98         if (!ResetEvent(m_handle))
99                 throw Error(GetLastError(), "ResetEvent() failed");
100 }
101
102 // CaseListWriter
103
104 CaseListWriter::CaseListWriter (void)
105         : m_dst                 (INVALID_HANDLE_VALUE)
106         , m_cancelEvent (true, false)
107 {
108 }
109
110 CaseListWriter::~CaseListWriter (void)
111 {
112 }
113
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
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
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
202 FileReader::FileReader (ThreadedByteBuffer* dst)
203         : m_dstBuf              (dst)
204         , m_handle              (INVALID_HANDLE_VALUE)
205         , m_cancelEvent (false, false)
206 {
207 }
208
209 FileReader::~FileReader (void)
210 {
211 }
212
213 void FileReader::start (HANDLE file)
214 {
215         DE_ASSERT(!isStarted());
216
217         m_handle = file;
218
219         de::Thread::start();
220 }
221
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
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
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
352 TestLogReader::~TestLogReader (void)
353 {
354         if (m_logFile != INVALID_HANDLE_VALUE)
355                 CloseHandle(m_logFile);
356 }
357
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
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
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
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
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
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
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
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
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
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
617 void Process::terminate (void)
618 {
619         stopProcess(false);
620 }
621
622 void Process::kill (void)
623 {
624         stopProcess(true);
625 }
626
627 } // win32
628
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
638 Win32TestProcess::~Win32TestProcess (void)
639 {
640         delete m_process;
641 }
642
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
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
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
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
776 bool Win32TestProcess::isRunning (void)
777 {
778         if (m_process)
779                 return m_process->isRunning();
780         else
781                 return false;
782 }
783
784 int Win32TestProcess::getExitCode (void) const
785 {
786         if (m_process)
787                 return m_process->getExitCode();
788         else
789                 return -1;
790 }
791
792 } // xs