|
|
@@ -67,8 +67,10 @@ namespace CodeDweller { |
|
|
|
}
|
|
|
|
|
|
|
|
ChildStream::~ChildStream() {
|
|
|
|
if (isRunning()) {
|
|
|
|
try {
|
|
|
|
close();
|
|
|
|
} catch (std::exception &e) {
|
|
|
|
;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
@@ -345,21 +347,47 @@ namespace CodeDweller { |
|
|
|
|
|
|
|
void ChildStream::close() {
|
|
|
|
|
|
|
|
if (isDone()) {
|
|
|
|
if (!childStarted) {
|
|
|
|
throw std::logic_error("Child process was not started "
|
|
|
|
"when close() was called");
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
// Terminate the child if it's running.
|
|
|
|
if (isRunning()) {
|
|
|
|
|
|
|
|
}
|
|
|
|
std::string errorString;
|
|
|
|
bool errorOccurred = false;
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
if (!TerminateProcess(childProcess, terminateExitCode)) {
|
|
|
|
if (!TerminateProcess(childProcess, terminateExitCode)) {
|
|
|
|
errorString += (errorOccurred ? "\n" : "");
|
|
|
|
errorString += "Error terminating the child process: " +
|
|
|
|
getErrorText();
|
|
|
|
errorOccurred = true;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
if (kill(childPid, SIGTERM) != 0) {
|
|
|
|
if (kill(childPid, SIGKILL) != 0) {
|
|
|
|
errorString += (errorOccurred ? "\n" : "");
|
|
|
|
errorString += "Error terminating the child process: " +
|
|
|
|
getErrorText();
|
|
|
|
errorOccurred = true;
|
|
|
|
} else if (waitpid(childPid, NULL, 0) != childPid) {
|
|
|
|
errorString += (errorOccurred ? "\n" : "");
|
|
|
|
errorString += "Error waiting for the child process: " +
|
|
|
|
getErrorText();
|
|
|
|
errorOccurred = true;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
throw std::runtime_error("Error terminating the child process: " +
|
|
|
|
getErrorText());
|
|
|
|
|
|
|
|
if (errorOccurred) {
|
|
|
|
throw std::runtime_error(errorString);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reset.
|
|
|
|
init();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ChildStream::isDone() {
|
|
|
@@ -695,7 +723,7 @@ namespace CodeDweller { |
|
|
|
}
|
|
|
|
|
|
|
|
Child::CircularBuffer::CircularBuffer(size_t maxSize) :
|
|
|
|
buffer(maxSize),
|
|
|
|
buffer(maxSize + 1),
|
|
|
|
capacity(maxSize) {
|
|
|
|
|
|
|
|
iBegin = 0;
|
|
|
@@ -717,10 +745,17 @@ namespace CodeDweller { |
|
|
|
|
|
|
|
size_t Child::CircularBuffer::nFree() const {
|
|
|
|
|
|
|
|
if (iBegin <= iEnd) {
|
|
|
|
return capacity - (iEnd - iBegin);
|
|
|
|
size_t iLocalBegin = iBegin;
|
|
|
|
|
|
|
|
// Correct an invalid value.
|
|
|
|
if (iLocalBegin >= capacity + 1) {
|
|
|
|
iLocalBegin = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (iLocalBegin <= iEnd) {
|
|
|
|
return capacity - (iEnd - iLocalBegin);
|
|
|
|
}
|
|
|
|
return iBegin - iEnd;
|
|
|
|
return iLocalBegin - iEnd - 1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
@@ -798,7 +833,7 @@ namespace CodeDweller { |
|
|
|
Child::~Child() {
|
|
|
|
try {
|
|
|
|
close();
|
|
|
|
} catch (...) {
|
|
|
|
} catch (std::exception &e) {
|
|
|
|
;
|
|
|
|
}
|
|
|
|
}
|
|
|
@@ -849,7 +884,17 @@ namespace CodeDweller { |
|
|
|
throw std::logic_error("No child process is running.");
|
|
|
|
}
|
|
|
|
|
|
|
|
#warning Check that this is okay before locking
|
|
|
|
// The free space in the write buffer can be checked without
|
|
|
|
// locking the mutex because:
|
|
|
|
//
|
|
|
|
// 1) bufferCapacity is unchanging, and
|
|
|
|
//
|
|
|
|
// 2) nWriteBytes is only decreased by the writer thread.
|
|
|
|
//
|
|
|
|
// This means that the calculated free space can only increase by
|
|
|
|
// the action of the writer thread; the calculated free space is a
|
|
|
|
// minimum value. In the worst case, this method would return
|
|
|
|
// false unnecessarily.
|
|
|
|
if (data.size() > bufferCapacity - nWriteBytes) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
@@ -872,7 +917,8 @@ namespace CodeDweller { |
|
|
|
throw std::logic_error("No child process is running.");
|
|
|
|
}
|
|
|
|
|
|
|
|
#warning Check that this is okay before locking
|
|
|
|
// See the comment above regarding checking the free space in the
|
|
|
|
// write buffer without locking the mutex.
|
|
|
|
size_t nFree = bufferCapacity - nWriteBytes;
|
|
|
|
|
|
|
|
if (0 == nFree) {
|
|
|
@@ -911,7 +957,9 @@ namespace CodeDweller { |
|
|
|
|
|
|
|
data.clear();
|
|
|
|
|
|
|
|
#warning Check that this is okay before locking
|
|
|
|
// Whether the read circular buffer is empty can be checked
|
|
|
|
// without locking the mutex because:
|
|
|
|
//
|
|
|
|
if (readBuffer.empty()) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
@@ -945,14 +993,54 @@ namespace CodeDweller { |
|
|
|
// Terminate the child if it's running.
|
|
|
|
if (isRunning()) {
|
|
|
|
|
|
|
|
std::string errorString;
|
|
|
|
bool errorOccurred = false;
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
if (!CloseHandle(inputHandle)) {
|
|
|
|
errorString = "Error closing input from the child: " + getErrorText();
|
|
|
|
errorOccurred = true;
|
|
|
|
}
|
|
|
|
if (!CloseHandle(outputHandle)) {
|
|
|
|
errorString += (errorOccurred ? "\n" : "");
|
|
|
|
errorString += "Error closing output to the child: " + getErrorText();
|
|
|
|
errorOccurred = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!TerminateProcess(childProcess, terminateExitCode)) {
|
|
|
|
errorString += (errorOccurred ? "\n" : "");
|
|
|
|
errorString += "Error terminating the child process: " +
|
|
|
|
getErrorText();
|
|
|
|
errorOccurred = true;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
if (kill(childPid, SIGTERM) != 0) {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (kill(childPid, SIGKILL) != 0) {
|
|
|
|
errorString += (errorOccurred ? "\n" : "");
|
|
|
|
errorString += "Error terminating the child process: " +
|
|
|
|
getErrorText();
|
|
|
|
errorOccurred = true;
|
|
|
|
} else if (waitpid(childPid, NULL, 0) != childPid) {
|
|
|
|
errorString += (errorOccurred ? "\n" : "");
|
|
|
|
errorString += "Error waiting for the child process: " +
|
|
|
|
getErrorText();
|
|
|
|
errorOccurred = true;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
throw std::runtime_error("Error terminating the child process: " +
|
|
|
|
getErrorText());
|
|
|
|
|
|
|
|
if (errorOccurred) {
|
|
|
|
throw std::runtime_error(errorString);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (threadsAreRunning) {
|
|
|
@@ -965,6 +1053,7 @@ namespace CodeDweller { |
|
|
|
|
|
|
|
// Reset.
|
|
|
|
init();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Child::isRunning() const {
|
|
|
@@ -1416,19 +1505,27 @@ namespace CodeDweller { |
|
|
|
errorText += getErrorText();
|
|
|
|
break;
|
|
|
|
|
|
|
|
} else if (0 == nBytesRead) {
|
|
|
|
|
|
|
|
// EOF; child exited.
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Copy to the shared read buffer.
|
|
|
|
while ((nBytesRead > 0) && !stopFlag) {
|
|
|
|
|
|
|
|
int nBytesToPut;
|
|
|
|
int nBytesToPut, nBytesFree;
|
|
|
|
|
|
|
|
nBytesToPut = nBytesRead;
|
|
|
|
|
|
|
|
#warning Check thread safety
|
|
|
|
if (nBytesToPut > readBuffer.nFree()) {
|
|
|
|
nBytesToPut = readBuffer.nFree();
|
|
|
|
// Can be called in the reader thread without locking the
|
|
|
|
// mutex.
|
|
|
|
nBytesFree = readBuffer.nFree();
|
|
|
|
|
|
|
|
if (nBytesToPut > nBytesFree) {
|
|
|
|
nBytesToPut = nBytesFree;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nBytesToPut > 0) {
|