Browse Source

Implemented streams for Windows. Tested isDone() and terminate(). Started testing streams.

git-svn-id: https://svn.microneil.com/svn/CodeDweller/branches/adeniz_1@31 d34b734f-a00e-4b39-a726-e4eeb87269ab
adeniz_1
adeniz 10 years ago
parent
commit
f5c9822cd4
2 changed files with 379 additions and 45 deletions
  1. 244
    28
      child.cpp
  2. 135
    17
      child.hpp

+ 244
- 28
child.cpp View File

@@ -20,14 +20,17 @@
//==============================================================================
#include <iostream> // Temporary.
#include <stdexcept>
#include "child.hpp"
namespace CodeDweller {
Child::Child(std::vector<std::string> args) {
Child::Child(std::vector<std::string> args, size_t bufSize) :
readStreambuf(bufSize),
writeStreambuf(bufSize),
reader(&readStreambuf),
writer(&writeStreambuf) {
init();
@@ -45,33 +48,34 @@ namespace CodeDweller {
cmdline += args.back(); // Append last command-line argument.
}
Child::Child(std::string childpath) :
Child::Child(std::string childpath, size_t bufSize) :
readStreambuf(bufSize),
writeStreambuf(bufSize),
reader(&readStreambuf),
writer(&writeStreambuf),
cmdline(childpath) {
init();
}
Child::~Child() {
// Close handles.
}
void
Child::init() {
childStarted = false;
exitCodeObtainedFlag = false;
exitCode = 0;
}
std::istream *
Child::reader() {
return new std::istream(std::cin.rdbuf());
}
std::ostream *
Child::writer() {
return new std::ostream(std::cout.rdbuf());
}
void
Child::run() {
if (childStarted) {
throw std::logic_error("Child process was active when "
"run() was called");
}
// Set the bInheritHandle flag so pipe handles are inherited.
SECURITY_ATTRIBUTES securityAttributes;
@@ -81,7 +85,9 @@ namespace CodeDweller {
// Create a pipe for the child process's STDOUT.
HANDLE childStdOutAtChild;
HANDLE childStdOutAtParent;
HANDLE childStdInAtChild;
HANDLE childStdInAtParent;
int bufferSize = 0;
if (!CreatePipe(&childStdOutAtParent,
@@ -139,39 +145,59 @@ namespace CodeDweller {
startInfo.dwFlags |= STARTF_USESTDHANDLES;
// Create the child process.
bool createSuccess;
bool status;
createSuccess = CreateProcess(NULL,
(char *) cmdline.c_str(), // command line
NULL, // process security attributes
NULL, // primary thread security attributes
true, // handles are inherited
0, // creation flags
NULL, // use parent's environment
NULL, // use parent's current directory
&startInfo, // STARTUPINFO pointer
&processInfo); // receives PROCESS_INFORMATION
status = CreateProcess(NULL,
(char *) cmdline.c_str(), // command line
NULL, // process security attributes
NULL, // primary thread security attributes
true, // handles are inherited
0, // creation flags
NULL, // use parent's environment
NULL, // use parent's current directory
&startInfo, // STARTUPINFO pointer
&processInfo); // receives PROCESS_INFORMATION
// If an error occurs, exit the application.
if (!createSuccess ) {
if (!status ) {
throw std::runtime_error("Error from CreateProcess with "
"command line \"" + cmdline + "\": " +
getErrorText());
}
// Provide the stream buffers with the handles for communicating
// with the child process.
readStreambuf.setInputHandle(childStdOutAtParent);
writeStreambuf.setOutputHandle(childStdInAtParent);
// Save the handles to the child process and its primary thread.
childProcess = processInfo.hProcess;
childThread = processInfo.hThread;
childStarted = true;
// Close the child's end of the pipes.
CloseHandle(childStdOutAtChild);
CloseHandle(childStdInAtChild);
if (!CloseHandle(childStdOutAtChild)) {
throw std::runtime_error("Error closing the child process stdout handle: " +
getErrorText());
}
if (!CloseHandle(childStdInAtChild)) {
throw std::runtime_error("Error closing the child process stdin handle: " +
getErrorText());
}
}
void
Child::terminate() {
if (isDone()) {
return;
}
if (!TerminateProcess(childProcess, terminateExitCode)) {
throw std::runtime_error("Error terminating the child process: " +
getErrorText());
@@ -179,9 +205,55 @@ namespace CodeDweller {
}
bool
Child::isDone() {
if (exitCodeObtainedFlag) {
return true;
}
if (!childStarted) {
throw std::logic_error("Child process was not started "
"when isDone() was called");
}
int result;
if (!GetExitCodeProcess(childProcess, (LPDWORD) &result)) {
throw std::runtime_error("Error checking status of child process: " +
getErrorText());
}
if (STILL_ACTIVE == result) {
return false;
}
// Child process has exited. Save the exit code.
exitCode = result;
exitCodeObtainedFlag = true;
return true;
}
int32_t
Child::result() {
if (exitCodeObtainedFlag) {
return exitCode;
}
if (!childStarted) {
throw std::logic_error("Child process was not started "
"when result() was called");
}
int result;
if (!GetExitCodeProcess(childProcess, (LPDWORD) &result)) {
@@ -189,6 +261,16 @@ namespace CodeDweller {
getErrorText());
}
// Error if the process has not exited.
if (STILL_ACTIVE == result) {
throw std::logic_error("Child process was active when "
"result() was called");
}
// Child process has exited. Save the exit code.
exitCode = result;
exitCodeObtainedFlag = true;
return result;
}
@@ -214,4 +296,138 @@ namespace CodeDweller {
return errMsg;
}
Child::ReadStreambuf::ReadStreambuf(std::size_t bufferSize) :
inputHandle(0),
buffer(bufferSize + 1) {
char *end = &(buffer.front()) + buffer.size();
// Indicate to underflow that underflow has not been called.
setg(end, end, end);
}
void
Child::ReadStreambuf::setInputHandle(HANDLE inHandle) {
inputHandle = inHandle;
}
std::streambuf::int_type
Child::ReadStreambuf::underflow() {
// Check for empty buffer.
if (gptr() < egptr()) {
// Not empty.
return traits_type::to_int_type(*gptr());
}
// Need to fill the buffer.
char *base = &(buffer.front());
char *start = base;
// Check whether this is the first fill.
if (eback() == base) {
// Not the first fill. Copy putback characters.
}
// start points to the start of the buffer. Fill buffer.
DWORD nBytesRead;
if (!ReadFile(inputHandle,
start,
buffer.size() - (start - base),
&nBytesRead,
NULL)) {
return traits_type::eof();
}
// Check for EOF.
if (0 == nBytesRead) {
return traits_type::eof();
}
// Update buffer pointers.
setg(base, start, start + nBytesRead);
return traits_type::to_int_type(*gptr());
}
Child::WriteStreambuf::WriteStreambuf(std::size_t bufferSize) :
outputHandle(0),
buffer(bufferSize + 1) {
char *base = &(buffer.front());
// Indicate to overflow that overflow has not been called.
setp(base, base + buffer.size() - 1);
}
void
Child::WriteStreambuf::setOutputHandle(HANDLE outHandle) {
outputHandle = outHandle;
}
void
Child::WriteStreambuf::flushBuffer() {
// Write.
std::ptrdiff_t nBytes = pptr() - pbase();
DWORD nBytesWritten;
if (!WriteFile(outputHandle,
pbase(),
nBytes,
&nBytesWritten,
NULL)) {
throw std::runtime_error("Error writing to child process: " +
getErrorText());
}
if (nBytes != nBytesWritten) {
throw std::runtime_error("Not all data was written to to child process: " +
getErrorText());
}
pbump(-nBytes);
return;
}
std::streambuf::int_type
Child::WriteStreambuf::overflow(int_type ch) {
// Check whether we're writing EOF.
if (traits_type::eof() != ch) {
// Not writing EOF.
*(pptr()) = ch;
pbump(1);
}
// Write.
flushBuffer();
// Success.
return traits_type::not_eof('a');
}
int
Child::WriteStreambuf::sync() {
flushBuffer();
return 1; // Success.
}
}

+ 135
- 17
child.hpp View File

@@ -31,6 +31,7 @@
#include <windows.h>

#include <cstdint>
#include <streambuf>
#include <istream>
#include <ostream>
#include <string>
@@ -56,6 +57,100 @@ namespace CodeDweller {

class Child {

private:

/// Streambuf class for reading the standard output of the child
/// process.
class ReadStreambuf : public std::streambuf {

public:

/// Reader streambuf constructor.
//
// \param[in] bufferSize is the size in bytes of the input
// buffer.
//
explicit ReadStreambuf(std::size_t bufferSize = 4096);

/// Set the handle to read the standard output of the child
/// process.
//
// \param[in] inHandle is the input handle for the standard
// output of the child process.
//
void setInputHandle(HANDLE inHandle);

private:

/// Override streambuf::underflow().
int_type underflow();

/// Copy constructor not implemented.
ReadStreambuf(const ReadStreambuf &);

/// Copy constructor not implemented.
ReadStreambuf &operator=(const ReadStreambuf &);

/// Input handle.
HANDLE inputHandle;

/// Read buffer.
std::vector<char> buffer;

};

/// Streambuf class for writing to the standard input of the child
/// process.
class WriteStreambuf : public std::streambuf {

public:

/// Writeer streambuf constructor.
//
// \param[in] bufferSize is the size in bytes of the input
// buffer.
//
explicit WriteStreambuf(std::size_t bufferSize = 4096);

/// Set the handle to write the standard input of the child
/// process.
//
// \param[in] outHandle is the output handle for the standard
// input of the child process.
//
void setOutputHandle(HANDLE outHandle);

private:

/// Flush the output buffer.
void flushBuffer();

/// Override streambuf::overflow().
int_type overflow(int_type ch);

/// Override streambuf::sync().
int sync();

/// Copy constructor not implemented.
WriteStreambuf(const WriteStreambuf &);

/// Copy constructor not implemented.
WriteStreambuf &operator=(const WriteStreambuf &);

/// Output handle.
HANDLE outputHandle;

/// Write buffer.
std::vector<char> buffer;

};

/// Stream buffer for reading to the stdout of the child process;
ReadStreambuf readStreambuf;

/// Stream buffer for writing to the stdin of the child process;
WriteStreambuf writeStreambuf;

public:

/** Constructor for spawning with command-line parameters.
@@ -68,8 +163,11 @@ namespace CodeDweller {
executable, and args[1] thru args[n] are the command-line
parameters.

\param[in] bufSize is the buffer size of the reader and writer
streams used to communicate with the child process.

*/
Child(std::vector<std::string> args);
Child(std::vector<std::string> args, size_t bufSize = 4096);

/** Constructor for spawning without command-line parameters.

@@ -78,37 +176,59 @@ namespace CodeDweller {

\param[in] childpath contains the child executable file name.

\param[in] bufSize is the buffer size of the reader and writer
streams used to communicate with the child process.

*/
Child(std::string childpath);
Child(std::string childpath, size_t bufSize = 4096);

/** Destructor terminates the child process. */
~Child();

/// Return a stream that is seen by the child as standard output.
std::istream *reader();
/// Input stream to read data from the child's standard output.
std::istream reader;

/// Return a stream that is seen by the child as standard input.
std::ostream *writer();
/// Output stream to write data to the child's standard input.
std::ostream writer;

/** Spawn the child process.

If an error occurs, an exception is thrown.
\throws runtime_error if an error occurs.

*/
void run();

/** Terminite the child process.

If an error occurs, an exception is thrown.
\throws runtime_error if an error occurs.

\throws logic_error if the child process is not running.

*/
void terminate();

/** Check whether the child process has exited.

\returns True if the child process has exited, false
otherwise.

\throws runtime_error if an error occurs.

\throws logic_error if the child process is not running.

*/
bool isDone();

/** Get the exit value of the child process.

\returns The exit value of the child process if the child
process has exited. If the child process has not exited, an
exception is thrown.
process has exited.

\throws runtime_error if an error occurs.

\throws logic_error if the child process has not exited.

\throws logic_error if the child process is not running.

*/
int32_t result();
@@ -118,18 +238,15 @@ namespace CodeDweller {
/// Exit code to use when terminating the child process.
static const uint32_t terminateExitCode = 0;

/// True if the child process was successfully started.
bool childStarted;

/// Initialize data members.
void init();

/// Child executable path and command-line parameters.
std::string cmdline;

/// Parent's read handle for child process standard output.
HANDLE childStdOutAtParent;

/// Parent's write handle for child process standard input.
HANDLE childStdInAtParent;

/// Child's process handle.
HANDLE childProcess;

@@ -146,7 +263,8 @@ namespace CodeDweller {
//
// \returns Human-readable description of the most recent error.
//
std::string getErrorText();
static std::string getErrorText();

};

}

Loading…
Cancel
Save