123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347 |
- // \file child.hpp
- //
- // Copyright (C) 2014 MicroNeil Research Corporation.
- //
- // This program is part of the MicroNeil Research Open Library Project. For
- // more information go to http://www.microneil.com/OpenLibrary/index.html
- //
- // This program is free software; you can redistribute it and/or modify it
- // under the terms of the GNU General Public License as published by the
- // Free Software Foundation; either version 2 of the License, or (at your
- // option) any later version.
- //
- // This program is distributed in the hope that it will be useful, but WITHOUT
- // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- // more details.
- //
- // You should have received a copy of the GNU General Public License along with
- // this program; if not, write to the Free Software Foundation, Inc., 59 Temple
- // Place, Suite 330, Boston, MA 02111-1307 USA
- //==============================================================================
-
- /*
- \brief The child module provides classes to spawn and communicate
- with child processes.
- */
-
- #ifndef CHILD_HPP
- #define CHILD_HPP
-
- #ifdef _WIN32
- #include <windows.h>
- #endif
-
- #include <cstdint>
- #include <streambuf>
- #include <iostream>
- #include <string>
- #include <vector>
- #include <thread>
- #include <mutex>
-
- namespace CodeDweller {
-
- /**
- \namespace CodeDweller
-
- The CodeDweller namespace contains components providing high-level
- functionality for applications.
-
- */
-
- /** Class that abstracts the iostreams communication with a child
- process.
-
- This class provides functionality to create a child process,
- communicate with the child process via streams and signals, and
- obtain the exit code of the child process.
-
- */
-
- class ChildStream : public std::iostream {
-
- private:
-
- /// Streambuf class for reading from the standard output and
- /// writing to the standard input of the child process.
- class ChildStreambuf : public std::streambuf {
-
- friend class ChildStream;
-
- public:
-
- /// Constructor.
- //
- // \param[in] bufSize is the size in bytes of the input
- // buffer and output buffer.
- //
- explicit ChildStreambuf(std::size_t bufSize = 4096);
-
- #ifdef _WIN32
-
- /// 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);
-
- /// 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);
-
- #else
-
- /// Set the file descriptor to read the standard output of the
- /// child process.
- //
- // \param[in] inFd is the input file descriptor for the standard
- // output of the child process.
- //
- void setInputFileDescriptor(int inFd);
-
- /// Set the file descriptor to write the standard input of the
- /// child process.
- //
- // \param[in] outFd is the output file descriptor for the
- // standard input of the child process.
- //
- void setOutputFileDescriptor(int outFd);
-
- #endif
-
- private:
-
- /** Return the number of bytes that can be read without
- blocking.
-
- This method checks if any input is available from the pipe,
- and returns the number of bytes in the input buffer plus 1.
- Reading that number of bytes will not block. Reading a
- larger number of bytes might block.
-
- \returns minimum number of bytes that can be read without
- blocking.
-
- */
- size_t numBytesAvailable() const;
-
- /// Override streambuf::underflow().
- int_type underflow();
-
- /// Flush the output buffer.
- void flushBuffer();
-
- /// Override streambuf::overflow().
- int_type overflow(int_type ch);
-
- /// Override streambuf::sync().
- int sync();
-
- /// Input and output handles.
- #ifdef _WIN32
- HANDLE inputHandle;
- HANDLE outputHandle;
- #else
- int inputFileDescriptor;
- int outputFileDescriptor;
- #endif
-
- /// Size of buffers.
- std::size_t bufferSize;
-
- /// Read buffer.
- std::vector<char> readBuffer;
-
- /// Write buffer.
- std::vector<char> writeBuffer;
-
- /// Copy constructor not implemented.
- ChildStreambuf(const ChildStreambuf &) = delete;
-
- /// Assignment operator not implemented.
- ChildStreambuf &operator=(const ChildStreambuf &) = delete;
-
- };
-
- /// Stream buffer for reading from the stdout and writing to the
- /// stdin of the child process.
- ChildStreambuf childStreambuf;
-
- public:
-
- /** Constructor for spawning with command-line parameters.
-
- The constructor configures the object, and spawns the child
- process.
-
- \param[in] args contains the child executable file name and
- command-line parameters. args[0] contains the full path of the
- executable, and args[1] thru args[n] are the command-line
- parameters.
-
- \param[in] bufSize is the input and output buffer size of the
- stream used to communicate with the child process.
-
- \throws runtime_error if an error occurs.
-
- */
- ChildStream(std::vector<std::string> const &args, size_t bufSize = 4096);
-
- /** Constructor for spawning without command-line parameters.
-
- The constructor configures the object, and spawns the child
- process.
-
- \param[in] childpath contains the child executable file name.
-
- \param[in] bufSize is the input and output buffer size of the
- stream used to communicate with the child process.
-
- \throws runtime_error if an error occurs.
-
- */
- ChildStream(std::string const &childpath, size_t bufSize = 4096);
-
- /** Constructor.
-
- The constructor configures the I/O buffers, but doesn't spawn
- any child process.
-
- \param[in] bufSize is the input and output buffer size of the
- stream used to communicate with the child process.
-
- */
- ChildStream(size_t bufSize = 4096);
-
- /** Destructor terminates the child process. */
- ~ChildStream();
-
- /** Spawn the child process.
-
- \param[in] args contains the child executable file name and
- command-line parameters. args[0] contains the full path of the
- executable, and args[1] thru args[n] are the command-line
- parameters.
-
- \throws runtime_error if an error occurs.
-
- \throws runtime_error if an error occurs.
-
- */
- void open(std::vector<std::string> const &args);
-
- /** Spawn the child process.
-
- \param[in] childpath contains the child executable file name.
-
- \throws runtime_error if an error occurs.
-
- */
- void open(std::string const &childpath);
-
- /** Get the number of bytes available for input.
-
- @returns number of bytes that can be read without blocking.
-
- */
- size_t numBytesAvailable() const;
-
- /** Check whether the child process is running.
-
- \returns True if the child process is running, false
- otherwise.
-
- */
- bool isRunning() const;
-
- /** Terminate the child process.
-
- \throws runtime_error if an error occurs.
-
- \throws logic_error if the child process is not running.
-
- */
- void close();
-
- /** 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.
-
- \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();
-
- private:
-
- /** Spawn the child process.
-
- \throws runtime_error if an error occurs.
-
- */
- void run();
-
- /// 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;
-
- /// True if the child process has exited.
- bool childExited;
-
- /// Initialize data members.
- void init();
-
- /// Child executable path and command-line parameters.
- std::vector<std::string> cmdArgs;
-
- #ifdef _WIN32
- /// Child's process handle.
- HANDLE childProcess;
-
- /// Child's thread handle.
- HANDLE childThread;
- #else
- /// Child process ID.
- pid_t childPid;
- #endif
-
- /// Exit value of the process.
- int32_t exitCode;
-
- /// True if the exit code has been obtained.
- bool exitCodeObtainedFlag;
-
- /// Return text for the most recent error.
- //
- // \returns Human-readable description of the most recent error.
- //
- static std::string getErrorText();
-
- };
-
- /** Class that abstracts the non-blocking communication with a child
- process.
-
- This class provides functionality to create a child process,
- communicate with the child process via non-blocking methods, and
- obtain the exit code of the child process.
-
- The existing process can be terminated by the close() method,
- and a new process spawned by the open() method.
-
- The close() method calls TerminateProcess under Windows, and
- sends SIGKILL under Linux. After sending SIGKILL, the close()
- methods calls waitpid() to wait for the child process to exit.
-
- When a child process is spawned, this class creates two threads:
- One for writing to the child, and one for reading from the
- child. These threads communicate with the user thread via:
-
- <ol>
-
- <li> A shared boolean that indicates when the threads should
- stop.</li>
-
- <li> A shared linear buffer containing data to write to the
- child process.</li>
-
- <li> A shared circular buffer containing data read from the
- child process.</li>
-
- </ol>
-
- */
-
- class Child {
-
- private:
-
- /** Circular buffer of characters. */
- class CircularBuffer {
-
- public:
-
- /** Constructor specifies the capacity.
-
- @param[in] maxSize is the capacity of the buffer.
-
- */
- CircularBuffer(size_t maxSize);
-
- /** Check whether the container is empty.
-
- @returns true if the container is empty, false otherwise.
-
- This method can be invoked by the user thread without
- serializing access to the object with a mutex. The
- reasoning is:
-
- 1) The buffer is empty if and only if iBegin == iEnd.
-
- 2) Only user thread modifies iBegin, by extracting data
- from the buffer.
-
- This means that if iBegin == iEnd, then:
-
- 1) iBegin is a valid index, since only the user thread can
- modify iBegin. The user thread maintains the validity
- of iBegin.
-
- 2) iEnd is a valid index, since it equals iBegin.
-
- 3) The result iBegin == iEnd is also valid, and indicates
- whether the buffer is empty.
-
- */
- bool empty() const;
-
- /** Get the size.
-
- @returns the number of bytes in the buffer.
-
- */
- size_t nUsed() const;
-
- /** Get the available space.
-
- @returns a number of bytes that can be written to the buffer
- without overwriting any existing data.
-
- This method can be invoked by the reader thread without
- serializing access to the object with a mutex. The reason
- is that:
-
- 1) The free space depends on capacity, iBegin, and iEnd.
-
- 2) The capacity is not changed while the threads are
- running, and only the reader thread modifies iEnd.
- Therefore, the reader thread always sees a valid and
- up-to-date value for capacity and iEnd.
-
- 3) Because the user thread modifies iBegin, iBegin might
- be invalid. The only invalid value is capacity + 1, in
- which case the correct value of iBegin is 0. This method
- checks for the invalid value, and uses the correct
- value as needed.
-
- 4) Because the user thread modifies iBegin, iBegin might
- be out-of-date. Because the user thread only
- increments iBegin, an out-of-date value would result in
- a smaller value of the available space in the buffer.
-
- */
- size_t nFree() const;
-
- /** Clear the buffer. */
- void clear();
-
- /** Put bytes to the buffer.
-
- @param[in] ptr is the address of the first byte to put.
-
- @param[in] nBytes is the number of bytes to put.
-
- @warning The capacity of the buffer must not be exceeded;
- exceeding the capacity corrupts the buffer.
-
- */
- void put(char const *ptr, size_t nBytes);
-
- /** Get bytes from the buffer.
-
- This method gets the specified number of bytes from the
- buffer, and erases those bytes from the buffer.
-
- The type T must have the following methods:
-
- <ol>
-
- <li>clear().</li>
-
- <li>capacity().</li>
-
- <li>reserve().</li>
-
- <li>push_back(char).</li>
-
- </ol>
-
- Both std::vector<char> and std::string can be used for T.
-
- @param[out] buf receives the data. The contents of buf are
- replaced with the data in the circular buffer.
-
- @param[in] nBytes is the number of bytes to get. Specifying
- a value of zero for nBytes gets and erases all the data.
-
- */
- template<typename T>
- void getAndErase(T &buf, size_t nBytes) {
-
- if ( (0 == nBytes) || (nBytes > nUsed()) ) {
- nBytes = nUsed();
- }
-
- buf.clear();
- if (buf.capacity() < nBytes) {
- buf.reserve(nBytes);
- }
-
- for (size_t i = 0; i < nBytes; i++) {
- buf.push_back(buffer[iBegin]);
- nextIndex(iBegin);
- }
-
- }
-
- /** Check whether specified data is present in the buffer.
-
- This method check whether the specified data is present in
- the buffer, and provides the number of characters before the
- specified data.
-
- The type U must have the following methods:
-
- <ol>
-
- <li>Methods required by CircularBuffer::match().</li>
-
- <li>size().</li>
-
- </ol>
-
- Both std::vector<char> and std::string can be used for T.
-
- @param[in] data is the specified data.
-
- @param[out] nBytesBefore is the number of bytes before the
- specified data.
-
- @returns true if the data is present in the buffer, false
- otherwise.
-
- @see CircularBuffer::match().
-
- */
- template<typename U>
- bool dataIsPresent(U const &data, size_t &nBytesBefore) const {
-
- size_t iLocalBegin = iBegin;
-
- if (nUsed() < data.size()) {
- return false;
- }
-
- size_t nToCheck = nUsed() - data.size() + 1;
- for (nBytesBefore = 0; nBytesBefore < nToCheck; nBytesBefore++) {
-
- if (match(data, iLocalBegin)) {
- return true;
- }
-
- nextIndex(iLocalBegin);
- }
-
- return false;
-
- }
-
- private:
-
- /** Increment the index.
-
- @param[in] index is the index to increment.
-
- */
- void nextIndex(size_t &index) const {
- index++;
- if (index >= capacity + 1)
- index = 0;
- }
-
-
- /** Check whether the specified data is in the buffer.
-
- This method checks whether the specified data is present in
- the buffer starting at the specified location.
-
- The type U must have the following methods:
-
- <ol>
-
- <li>operator[].</li>
-
- <li>size().</li>
-
- </ol>
-
- @param[in] data is the specified data.
-
- @param[in] index is the index of the buffer to start
- checking for the specified data.
-
- @returns true if the specified data is in the buffer, false
- otherwise.
-
- */
- template<typename U>
- bool match(U &data, size_t indx) const {
-
- for (size_t i = 0; i < data.size(); i++) {
-
- if (iEnd == indx) {
- return false;
- }
- if (data[i] != buffer[indx]) {
- return false;
- }
- nextIndex(indx);
-
- }
-
- return true;
-
- }
-
- /// Buffer to hold data.
- std::vector<char> buffer;
-
- /// Capacity of the buffer.
- size_t capacity;
-
- /// Index of first element.
- size_t iBegin;
-
- /// Index of one past the last element.
- size_t iEnd;
-
- };
-
- public:
-
- /** Constructor for spawning with command-line parameters.
-
- The constructor configures the object, and spawns the child
- process.
-
- \param[in] args contains the child executable file name and
- command-line parameters. args[0] contains the full path of the
- executable, and args[1] thru args[n] are the command-line
- parameters.
-
- \param[in] bufSize is the input and output buffer size of the
- stream used to communicate with the child process.
-
- \param[in] nominalAboveMinPollTime_ms is used to determine the
- minimum time in milliseconds that the writer thread sleeps
- when there's no data in the output buffer, and that the reader
- thread sleeps when there's no room in the input buffer. The
- minimum time is nominalAboveMinPollTime_ms +
- CodeDweller::MinimumSleeperTime.
-
- \param[in] deltaPollTime_ms is how much longer, in
- milliseconds, the maximum time to sleep is than the minimum time.
-
- \throws runtime_error if an error occurs.
-
- */
- Child(std::vector<std::string> const &args,
- size_t bufSize = 128 * 1024,
- std::uint16_t nominalAboveMinPollTime_ms = 0,
- std::uint16_t deltaPollTime_ms = 500);
-
- /** Constructor for spawning without command-line parameters.
-
- The constructor configures the object, and spawns the child
- process.
-
- \param[in] childpath contains the child executable file name.
-
- \param[in] bufSize is the input and output buffer size of the
- stream used to communicate with the child process.
-
- \param[in] nominalAboveMinPollTime_ms is used to determine the
- minimum time in milliseconds that the writer thread sleeps
- when there's no data in the output buffer, and that the reader
- thread sleeps when there's no room in the input buffer. The
- minimum time is nominalAboveMinPollTime_ms +
- CodeDweller::MinimumSleeperTime.
-
- \param[in] deltaPollTime_ms is how much longer, in
- milliseconds, the maximum time to sleep is than the minimum time.
-
- \throws runtime_error if an error occurs.
-
- */
- Child(std::string const &childpath,
- size_t bufSize = 128 * 1024,
- std::uint16_t nominalAboveMinPollTime_ms = 0,
- std::uint16_t deltaPollTime_ms = 500);
-
- /** Constructor.
-
- The constructor configures the I/O buffers, but doesn't spawn
- any child process.
-
- \param[in] bufSize is the input and output buffer size of the
- stream used to communicate with the child process.
-
- \param[in] nominalAboveMinPollTime_ms is used to determine the
- minimum time in milliseconds that the writer thread sleeps
- when there's no data in the output buffer, and that the reader
- thread sleeps when there's no room in the input buffer. The
- minimum time is nominalAboveMinPollTime_ms +
- CodeDweller::MinimumSleeperTime.
-
- \param[in] deltaPollTime_ms is how much longer, in
- milliseconds, the maximum time to sleep is than the minimum time.
-
- */
- Child(size_t bufSize = 4096,
- std::uint16_t nominalAboveMinPollTime_ms = 0,
- std::uint16_t deltaPollTime_ms = 500);
-
- /** Destructor terminates the child process. */
- ~Child();
-
- /** Spawn the child process.
-
- \param[in] args contains the child executable file name and
- command-line parameters. args[0] contains the full path of the
- executable, and args[1] thru args[n] are the command-line
- parameters.
-
- \throws runtime_error if an error occurs.
-
- \throws runtime_error if an error occurs.
-
- */
- void open(std::vector<std::string> const &args);
-
- /** Spawn the child process.
-
- \param[in] childpath contains the child executable file name.
-
- \throws runtime_error if an error occurs.
-
- */
- void open(std::string const &childpath);
-
- /** All-or-nothing non-blocking queue write request to the child.
-
- This methods attempts to queue a write request consisting of
- the entire contents of a container.
-
- The type T must have the following methods:
-
- <ol>
-
- <li>begin().</li>
-
- <li>end().</li>
-
- <li>size().</li>
-
- </ol>
-
- Both std::vector<char> and std::string can be used for T.
-
- @param[in] data is the data to queue.
-
- @returns true if the write request was queued, false
- otherwise.
-
- */
- template<typename T>
- bool write(T const &data) {
-
- if (!isRunning()) {
- throw std::logic_error("No child process is running.");
- }
-
- // 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;
- }
-
- std::lock_guard<std::mutex> lock(writeBufferMutex);
-
- std::copy(data.begin(),
- data.end(),
- &(writeBuffer[nWriteBytes]));
-
- nWriteBytes += data.size();
-
- return true;
-
- }
-
- /** All-or-nothing non-blocking queue write request to the child.
-
- This methods attempts to queue a write request consisting of
- the entire contents of string literal.
-
- @param[in] data points to the string literal.
-
- @returns true if the write request was queued, false
- otherwise.
-
- */
- bool write(char const *data) {
- return write(std::string(data));
- }
-
- /** Non-blocking queue write request to the child.
-
- This methods attempts to queue a write request consisting of
- the the specified contents of a container. The number of
- bytes queued is the smaller of the size of the container and
- the number of free bytes in the output buffer.
-
- The type T must have the following methods:
-
- <ol>
-
- <li>begin().</li>
-
- <li>end().</li>
-
- <li>size().</li>
-
- </ol>
-
- Both std::vector<char> and std::string can be used for T.
-
- @param[in] data is the data to queue.
-
- @param[in] nBytes is the requested number of bytes to queue.
-
- @returns the number of bytes queued.
-
- */
- template<typename T>
- size_t write(T const &data, size_t nBytes) {
-
- if (!isRunning()) {
- throw std::logic_error("No child process is running.");
- }
-
- // See the comment above regarding checking the free space in the
- // write buffer without locking the mutex.
- size_t nFree = bufferCapacity - nWriteBytes;
-
- if (nBytes > nFree) {
- nBytes = nFree;
- }
- if (nBytes > data.size()) {
- nBytes = data.size();
- }
-
- std::lock_guard<std::mutex> lock(writeBufferMutex);
-
- std::copy(data.begin(),
- data.begin() + nBytes,
- &(writeBuffer[nWriteBytes]));
-
- nWriteBytes += nBytes;
-
- return nBytes;
-
- }
-
- /** Non-blocking queue write request to the child.
-
- This methods attempts to queue a write request consisting of
- as much as possible of the contents of the string.
-
- The type T must have the following methods:
-
- <ol>
-
- <li>begin().</li>
-
- <li>size().</li>
-
- </ol>
-
- Both std::vector<char> and std::string can be used for T.
-
- @param[in, out] data on input is the data to queue. On
- output, data contains the data that was not queued.
-
- @returns the number of bytes queued.
-
- */
- template<typename T>
- size_t writeAndShrink(T &data) {
- if (!isRunning()) {
- throw std::logic_error("No child process is running.");
- }
-
- // 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) {
- return 0;
- }
-
- std::lock_guard<std::mutex> lock(writeBufferMutex);
-
- size_t nBytesToCopy = data.size();
-
- if (nBytesToCopy > nFree) {
- nBytesToCopy = nFree;
- }
-
- std::copy(data.begin(),
- data.begin() + nBytesToCopy,
- &(writeBuffer[nWriteBytes]));
-
- nWriteBytes += nBytesToCopy;
-
- data.erase(data.begin(), data.begin() + nBytesToCopy);
-
- return nBytesToCopy;
-
- }
-
- /** Check if all queued data was transmitted.
-
- @returns true if all the queued data was transmitted to the
- child, false otherwise.
-
- */
- bool isFinishedWriting() const;
-
- /** Non-blocking request to get data read from the child.
-
- This method attempts to get up to a specified number of bytes
- of data from the input buffer containing data received from
- the child. The data that is provided is erased from the input
- buffer.
-
- The type T must have the following methods:
-
- <ol>
-
- <li>Methods required by CircularBuffer::getAndErase().</li>
-
- <li>size().</li>
-
- </ol>
-
- Both std::vector<char> and std::string can be used for T.
-
- @param[out] data contains the copied data.
-
- @param[in] nBytes is the number of bytes to attempt to copy.
- If nBytes is zero, the contents of the entire input buffer is
- moved to data.
-
- @returns the number of bytes copied.
-
- @see CircularBuffer::getAndErase().
-
- */
- template<typename T>
- size_t read(T &data, size_t nBytes = 0) {
-
- if (!childStarted) {
- throw std::logic_error("Child process was not started.");
- }
-
- data.clear();
-
- // Can be called in the user thread without locking the mutex.
- if (readBuffer.empty()) {
- return 0;
- }
-
- std::lock_guard<std::mutex> lock(readBufferMutex);
-
- size_t nBytesToRead = nBytes;
-
- if (nBytesToRead > readBuffer.nUsed()) {
- nBytesToRead = readBuffer.nUsed();
- }
-
- readBuffer.getAndErase(data, nBytesToRead);
-
- return data.size();
-
- }
-
- /** Non-blocking request to get data up to a delimiter read from
- the child.
-
- This method check whether the specified delimiter is in the
- data received from the child.
-
- This method attempts to get data up to and not including a
- specified delimiter from the input buffer containing data
- received from the child. The data that is provided and the
- delimiter are erased from the input buffer.
-
- The type T must have the following methods:
-
- <ol>
-
- <li>Methods required by CircularBuffer::getAndErase().</li>
-
- <li>size().</li>
-
- <li>clear().</li>
-
- </ol>
-
- The type U must have the following methods:
-
- <ol>
-
- <li>Methods required by CircularBuffer::dataIsPresent().</li>
-
- <li>size().</li>
-
- <li>empty().</li>
-
- </ol>
-
- Both std::vector<char> and std::string can be used for T and U.
-
- @param[out] data contains the copied data.
-
- @param[in] delimiter contains the delimiter.
-
- @returns the number of bytes copied.
-
- @see CircularBuffer::getAndErase().
-
- @see CircularBuffer::dataIsPresent().
-
- */
- template<typename T, typename U>
- bool readDelimited(T &data, U const &delimiter) {
-
- if (!childStarted) {
- throw std::logic_error("Child process was not started.");
- }
-
- data.clear();
-
- // Can be called in the user thread without locking the mutex.
- if (readBuffer.empty()) {
- return false;
- }
-
- // Empty delimiter always matches.
- if (delimiter.empty()) {
- return true;
- }
-
- std::lock_guard<std::mutex> lock(readBufferMutex);
-
- size_t nBytesToRead;
-
- // Check for the presence of the delimiter.
- if (!readBuffer.dataIsPresent(delimiter, nBytesToRead)) {
- return false;
- }
-
- // Read the data.
- if (nBytesToRead > 0) {
- readBuffer.getAndErase(data, nBytesToRead);
- }
-
- // Discard the delimiter.
- T temp;
-
- readBuffer.getAndErase(temp, delimiter.size());
-
- return true;
-
- }
-
- /** Non-blocking request to get data up to a delimiter read from
- the child.
-
- This method check whether the specified delimiter is in the
- data received from the child.
-
- This method attempts to get data up to and not including a
- specified delimiter from the input buffer containing data
- received from the child. The data that is provided and the
- delimiter are erased from the input buffer.
-
- The type T must have the following methods:
-
- <ol>
-
- <li>Methods required by CircularBuffer::getAndErase().</li>
-
- <li>size().</li>
-
- <li>clear().</li>
-
- </ol>
-
- Both std::vector<char> and std::string can be used for T.
-
- @param[out] data contains the copied data.
-
- @param[in] delimiter contains the delimiter.
-
- @returns the number of bytes copied.
-
- @see CircularBuffer::getAndErase().
-
- */
- template<typename T>
- bool readDelimited(T &data, char const *delimiter) {
- return readDelimited(data, std::string(delimiter));
- }
-
- /** Check whether the child process is running.
-
- \returns True if the child process is running, false
- otherwise.
-
- */
- bool isRunning() const;
-
- /** Check error status.
-
- This method checks whether an error occurred when
- communicating with the child process.
-
- \param[out] errorDescription contains the description of any
- error that occurred.
-
- \returns true if an error occurred, false otherwise.
-
- */
- bool errorOccurred(std::string &errorDescription) const;
-
- /** Close the child process' standard input connection. */
- void closeStdIn();
-
- /** Close the connection.
-
- This method terminate the child process if it is running, and
- resets the object. After this method is invoked, open() can
- be invoked to spawn and communicate with another child
- process.
-
- \throws runtime_error if an error occurs.
-
- \throws logic_error if the child process is not running.
-
- */
- void close();
-
- /** 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 had not been started.
-
- */
- bool isDone();
-
- /** Get the exit value of the child process.
-
- \returns The exit value of the child process if the child
- 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 had not been started.
-
- */
- int32_t result();
-
- private:
-
- /// Initialize data members.
- void init();
-
- /** Spawn the child process.
-
- \throws runtime_error if an error occurs.
-
- */
- void run();
-
- /** Close the output connection to the child.
-
- \param[in, out] errMesg is updated with a description of any
- error that occurs.
-
- \returns true if the connection was closed successfully.
-
- */
- bool closeOutput(std::string &errMesg);
-
- /** Close the input connection to the child.
-
- \param[in, out] errMesg is updated with a description of any
- error that occurs.
-
- \returns true if the connection was closed successfully.
-
- */
- bool closeInput(std::string &errMesg);
-
- /// Reader thread object.
- std::thread readerThread;
-
- /// Thread start function to read data from the child.
- void readFromChild();
-
- /// Writer thread object.
- std::thread writerThread;
-
- /// Thread start function to send data to the child.
- void writeToChild();
-
- /// True if readerThread is to stop.
- bool stopReaderFlag;
-
- /// True if writerThread is to stop.
- bool stopWriterFlag;
-
- /// True if the reader thread is running, false otherwise.
- bool readerThreadIsRunning;
-
- /// True if the writer thread is running, false otherwise.
- bool writerThreadIsRunning;
-
- /// Description of any error.
- std::string errorText;
-
- /// Input and output handles.
- #ifdef _WIN32
- HANDLE inputHandle;
- HANDLE outputHandle;
- #else
- int inputFileDescriptor;
- int outputFileDescriptor;
- #endif
-
- /// Capacity of buffers.
- std::size_t bufferCapacity;
-
- /// Read buffer.
- CircularBuffer readBuffer;
-
- /// Mutex to serialize access to readBuffer.
- std::mutex readBufferMutex;
-
- /// Write buffer.
- std::vector<char> writeBuffer;
-
- /// Number of bytes in writeBuffer.
- size_t nWriteBytes;
-
- /// Mutex to serialize access to writeBuffer.
- std::mutex writeBufferMutex;
-
- /// Number of bytes in writer thread transmit buffer.
- size_t nTransmitBytes;
-
- /// Nominal poll time.
- //
- // If there isn't room in readBuffer, readerThread aperiodically
- // checks whether room is available. The check times are
- // determined by a CodeDweller::PollTimer object, which requires a
- // nominal poll time and a maximum poll time.
- int nominalPollTime_ms;
-
- /// Maximum poll time.
- int maximumPollTime_ms;
-
- /// 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;
-
- /// True if a system call indicates that the child process has
- /// exited.
- bool childExited;
-
- /// True if the child exited for any other reason.
- bool childExitedInferred;
-
- /// Child executable path and command-line parameters.
- std::vector<std::string> cmdArgs;
-
- #ifdef _WIN32
- /// Child's process handle.
- HANDLE childProcess;
-
- /// Child's thread handle.
- HANDLE childThread;
- #else
- /// Child process ID.
- pid_t childPid;
- #endif
-
- /// Exit value of the process.
- int32_t exitCode;
-
- /// True if the exit code has been obtained.
- bool exitCodeObtainedFlag;
-
- /// Return text for the most recent error.
- //
- // \returns Human-readable description of the most recent error.
- //
- static std::string getErrorText();
-
- };
-
- }
-
- #endif // CHILD_HPP
|