|
|
|
|
|
|
|
|
nWriteBytes = 0;
|
|
|
nWriteBytes = 0;
|
|
|
nTransmitBytes = 0;
|
|
|
nTransmitBytes = 0;
|
|
|
|
|
|
|
|
|
threadsAreRunning = false;
|
|
|
|
|
|
stopFlag = true;
|
|
|
|
|
|
|
|
|
readerThreadIsRunning = false;
|
|
|
|
|
|
writerThreadIsRunning = false;
|
|
|
|
|
|
stopReaderFlag = true;
|
|
|
|
|
|
stopWriterFlag = true;
|
|
|
errorText.clear();
|
|
|
errorText.clear();
|
|
|
|
|
|
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
(0 == nTransmitBytes) );
|
|
|
(0 == nTransmitBytes) );
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool Child::closeInput(std::string &errorString) {
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
|
// Close if not already closed.
|
|
|
|
|
|
if ( (NULL != inputHandle) &&
|
|
|
|
|
|
(CloseHandle(inputHandle) == 0) ) {
|
|
|
|
|
|
errorString += (errorString.empty() ? "" : "\n");
|
|
|
|
|
|
errorString += "Error closing input to the child: " + getErrorText();
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Closed successfully.
|
|
|
|
|
|
inputHandle = NULL;
|
|
|
|
|
|
return true;
|
|
|
|
|
|
#else
|
|
|
|
|
|
// Close if not already closed.
|
|
|
|
|
|
if ( (-1 != inputFileDescriptor) &&
|
|
|
|
|
|
(0 != ::close(inputFileDescriptor)) ) {
|
|
|
|
|
|
errorString += (errorString.empty() ? "" : "\n");
|
|
|
|
|
|
errorString += "Error closing input to the child: " + getErrorText();
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Closed successfully.
|
|
|
|
|
|
inputFileDescriptor = -1;
|
|
|
|
|
|
return true;
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool Child::closeOutput(std::string &errorString) {
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
|
// Close if not already closed.
|
|
|
|
|
|
if ( (NULL != outputHandle) &&
|
|
|
|
|
|
(CloseHandle(outputHandle) == 0) ) {
|
|
|
|
|
|
errorString += (errorString.empty() ? "" : "\n");
|
|
|
|
|
|
errorString += "Error closing output to the child: " + getErrorText();
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Closed successfully.
|
|
|
|
|
|
outputHandle = NULL;
|
|
|
|
|
|
return true;
|
|
|
|
|
|
#else
|
|
|
|
|
|
// Close if not already closed.
|
|
|
|
|
|
if ( (-1 != outputFileDescriptor) &&
|
|
|
|
|
|
(0 != ::close(outputFileDescriptor)) ) {
|
|
|
|
|
|
errorString += (errorString.empty() ? "" : "\n");
|
|
|
|
|
|
errorString += "Error closing output to the child: " + getErrorText();
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Closed successfully.
|
|
|
|
|
|
outputFileDescriptor = -1;
|
|
|
|
|
|
return true;
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Child::closeStdIn() {
|
|
|
|
|
|
|
|
|
|
|
|
if (!childStarted) {
|
|
|
|
|
|
throw std::logic_error("Child process was not started "
|
|
|
|
|
|
"when close() was called");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Stop writer thread. Note: None of the error conditions that
|
|
|
|
|
|
// cause an exception to be thrown by join() can ever occur.
|
|
|
|
|
|
stopWriterFlag = true;
|
|
|
|
|
|
|
|
|
|
|
|
std::string errorString;
|
|
|
|
|
|
|
|
|
|
|
|
if (!closeOutput(errorString)) {
|
|
|
|
|
|
throw std::runtime_error(errorString);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (writerThreadIsRunning) {
|
|
|
|
|
|
writerThread.join();
|
|
|
|
|
|
writerThreadIsRunning = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
void Child::close() {
|
|
|
void Child::close() {
|
|
|
|
|
|
|
|
|
if (!childStarted) {
|
|
|
if (!childStarted) {
|
|
|
|
|
|
|
|
|
// Stop the reader and writer threads. Note: None of the error
|
|
|
// Stop the reader and writer threads. Note: None of the error
|
|
|
// conditions that cause an exception to be thrown by join()
|
|
|
// conditions that cause an exception to be thrown by join()
|
|
|
// can ever occur.
|
|
|
// can ever occur.
|
|
|
stopFlag = true;
|
|
|
|
|
|
|
|
|
stopReaderFlag = true;
|
|
|
|
|
|
stopWriterFlag = true;
|
|
|
|
|
|
|
|
|
// Terminate the child if it's running.
|
|
|
// Terminate the child if it's running.
|
|
|
if (isRunning()) {
|
|
|
if (isRunning()) {
|
|
|
|
|
|
|
|
|
std::string errorString;
|
|
|
std::string errorString;
|
|
|
bool errorOccurred = false;
|
|
|
|
|
|
|
|
|
bool noErrorOccurred = true;
|
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
#ifdef _WIN32
|
|
|
// Ignore errors. Reason: Terminating a proces that doesn't
|
|
|
// Ignore errors. Reason: Terminating a proces that doesn't
|
|
|
// exist (e.g. has exited) gives an error.
|
|
|
// exist (e.g. has exited) gives an error.
|
|
|
(void) TerminateProcess(childProcess, terminateExitCode);
|
|
|
(void) TerminateProcess(childProcess, terminateExitCode);
|
|
|
|
|
|
|
|
|
if (CloseHandle(inputHandle) == 0) {
|
|
|
|
|
|
errorString = "Error closing input from the child: " + getErrorText();
|
|
|
|
|
|
errorOccurred = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (CloseHandle(outputHandle) == 0) {
|
|
|
|
|
|
errorString += (errorOccurred ? "\n" : "");
|
|
|
|
|
|
errorString += "Error closing output to the child: " + getErrorText();
|
|
|
|
|
|
errorOccurred = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
noErrorOccurred = noErrorOccurred && closeInput(errorString);
|
|
|
|
|
|
noErrorOccurred = noErrorOccurred && closeOutput(errorString);
|
|
|
#else
|
|
|
#else
|
|
|
if (0 != ::close(inputFileDescriptor)) {
|
|
|
|
|
|
errorString = "Error closing input from the child: " + getErrorText();
|
|
|
|
|
|
errorOccurred = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (0 != ::close(outputFileDescriptor)) {
|
|
|
|
|
|
errorString += (errorOccurred ? "\n" : "");
|
|
|
|
|
|
errorString += "Error closing output to the child: " + getErrorText();
|
|
|
|
|
|
errorOccurred = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
noErrorOccurred = noErrorOccurred && closeInput(errorString);
|
|
|
|
|
|
noErrorOccurred = noErrorOccurred && closeOutput(errorString);
|
|
|
|
|
|
|
|
|
if (kill(childPid, SIGKILL) != 0) {
|
|
|
if (kill(childPid, SIGKILL) != 0) {
|
|
|
errorString += (errorOccurred ? "\n" : "");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
errorString += (errorString.empty() ? "" : "\n");
|
|
|
errorString += "Error terminating the child process: " +
|
|
|
errorString += "Error terminating the child process: " +
|
|
|
getErrorText();
|
|
|
getErrorText();
|
|
|
errorOccurred = true;
|
|
|
|
|
|
|
|
|
noErrorOccurred = false;
|
|
|
|
|
|
|
|
|
} else if (waitpid(childPid, NULL, 0) != childPid) {
|
|
|
} else if (waitpid(childPid, NULL, 0) != childPid) {
|
|
|
errorString += (errorOccurred ? "\n" : "");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
errorString += (errorString.empty() ? "" : "\n");
|
|
|
errorString += "Error waiting for the child process: " +
|
|
|
errorString += "Error waiting for the child process: " +
|
|
|
getErrorText();
|
|
|
getErrorText();
|
|
|
errorOccurred = true;
|
|
|
|
|
|
|
|
|
noErrorOccurred = false;
|
|
|
|
|
|
|
|
|
}
|
|
|
}
|
|
|
#endif
|
|
|
#endif
|
|
|
|
|
|
|
|
|
if (errorOccurred) {
|
|
|
|
|
|
|
|
|
if (!noErrorOccurred) {
|
|
|
throw std::runtime_error(errorString);
|
|
|
throw std::runtime_error(errorString);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
if (threadsAreRunning) {
|
|
|
|
|
|
|
|
|
if (readerThreadIsRunning) {
|
|
|
readerThread.join();
|
|
|
readerThread.join();
|
|
|
|
|
|
readerThreadIsRunning = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (writerThreadIsRunning) {
|
|
|
writerThread.join();
|
|
|
writerThread.join();
|
|
|
threadsAreRunning = false;
|
|
|
|
|
|
|
|
|
writerThreadIsRunning = false;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
// Reset.
|
|
|
// Reset.
|
|
|
|
|
|
|
|
|
childStarted = true;
|
|
|
childStarted = true;
|
|
|
|
|
|
|
|
|
// Start the reader and writer threads.
|
|
|
// Start the reader and writer threads.
|
|
|
stopFlag = false;
|
|
|
|
|
|
|
|
|
stopReaderFlag = false;
|
|
|
|
|
|
stopWriterFlag = false;
|
|
|
|
|
|
|
|
|
try {
|
|
|
try {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} catch (std::exception &e) {
|
|
|
} catch (std::exception &e) {
|
|
|
|
|
|
|
|
|
stopFlag = true;
|
|
|
|
|
|
|
|
|
stopReaderFlag = true;
|
|
|
|
|
|
stopWriterFlag = true;
|
|
|
readerThread.join();
|
|
|
readerThread.join();
|
|
|
|
|
|
|
|
|
throw std::runtime_error("Error starting writer thread: " +
|
|
|
throw std::runtime_error("Error starting writer thread: " +
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
threadsAreRunning = true;
|
|
|
|
|
|
|
|
|
readerThreadIsRunning = true;
|
|
|
|
|
|
writerThreadIsRunning = true;
|
|
|
|
|
|
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
childExited = true;
|
|
|
childExited = true;
|
|
|
|
|
|
|
|
|
// Stop threads.
|
|
|
// Stop threads.
|
|
|
stopFlag = true;
|
|
|
|
|
|
readerThread.join();
|
|
|
|
|
|
writerThread.join();
|
|
|
|
|
|
threadsAreRunning = false;
|
|
|
|
|
|
|
|
|
stopReaderFlag = true;
|
|
|
|
|
|
stopWriterFlag = true;
|
|
|
|
|
|
if (readerThreadIsRunning) {
|
|
|
|
|
|
readerThread.join();
|
|
|
|
|
|
readerThreadIsRunning = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (writerThreadIsRunning) {
|
|
|
|
|
|
writerThread.join();
|
|
|
|
|
|
writerThreadIsRunning = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
return true;
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CodeDweller::PollTimer pollTimer(nominalPollTime_ms, maximumPollTime_ms);
|
|
|
CodeDweller::PollTimer pollTimer(nominalPollTime_ms, maximumPollTime_ms);
|
|
|
|
|
|
|
|
|
while (!stopFlag) {
|
|
|
|
|
|
|
|
|
while (!stopReaderFlag) {
|
|
|
|
|
|
|
|
|
char *bufferPtr;
|
|
|
char *bufferPtr;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
&nBytesRead,
|
|
|
&nBytesRead,
|
|
|
NULL)) {
|
|
|
NULL)) {
|
|
|
|
|
|
|
|
|
if (stopFlag) {
|
|
|
|
|
|
|
|
|
if (stopReaderFlag) {
|
|
|
break;
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bufferCapacity);
|
|
|
bufferCapacity);
|
|
|
if (-1 == nBytesRead) {
|
|
|
if (-1 == nBytesRead) {
|
|
|
|
|
|
|
|
|
if (stopFlag) {
|
|
|
|
|
|
|
|
|
if (stopReaderFlag) {
|
|
|
break;
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
#endif
|
|
|
|
|
|
|
|
|
// Copy to the shared read buffer.
|
|
|
// Copy to the shared read buffer.
|
|
|
while ((nBytesRead > 0) && !stopFlag) {
|
|
|
|
|
|
|
|
|
while ((nBytesRead > 0) && !stopReaderFlag) {
|
|
|
|
|
|
|
|
|
int nBytesToPut, nBytesFree;
|
|
|
int nBytesToPut, nBytesFree;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CodeDweller::PollTimer pollTimer(nominalPollTime_ms, maximumPollTime_ms);
|
|
|
CodeDweller::PollTimer pollTimer(nominalPollTime_ms, maximumPollTime_ms);
|
|
|
|
|
|
|
|
|
while (!stopFlag) {
|
|
|
|
|
|
|
|
|
while (!stopWriterFlag) {
|
|
|
|
|
|
|
|
|
char *bufferPtr;
|
|
|
char *bufferPtr;
|
|
|
|
|
|
|
|
|
// Poll for data in the shared write buffer.
|
|
|
// Poll for data in the shared write buffer.
|
|
|
while ((0 == nWriteBytes) && !stopFlag) {
|
|
|
|
|
|
|
|
|
while ((0 == nWriteBytes) && !stopWriterFlag) {
|
|
|
pollTimer.pause();
|
|
|
pollTimer.pause();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
if (stopFlag) {
|
|
|
|
|
|
|
|
|
if (stopWriterFlag) {
|
|
|
goto exit;
|
|
|
goto exit;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
nWriteBytes = 0;
|
|
|
nWriteBytes = 0;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
if (stopFlag) {
|
|
|
|
|
|
|
|
|
if (stopWriterFlag) {
|
|
|
goto exit;
|
|
|
goto exit;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
&nBytesWritten,
|
|
|
&nBytesWritten,
|
|
|
NULL)) {
|
|
|
NULL)) {
|
|
|
|
|
|
|
|
|
if (stopFlag) {
|
|
|
|
|
|
|
|
|
if (stopWriterFlag) {
|
|
|
goto exit;
|
|
|
goto exit;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
if (stopFlag) {
|
|
|
|
|
|
|
|
|
if (stopWriterFlag) {
|
|
|
goto exit;
|
|
|
goto exit;
|
|
|
}
|
|
|
}
|
|
|
#else
|
|
|
#else
|
|
|
|
|
|
|
|
|
bufferPtr,
|
|
|
bufferPtr,
|
|
|
nLocalWriteBytes);
|
|
|
nLocalWriteBytes);
|
|
|
|
|
|
|
|
|
if (stopFlag) {
|
|
|
|
|
|
|
|
|
if (stopWriterFlag) {
|
|
|
goto exit;
|
|
|
goto exit;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|