|
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186 |
- // \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);
-
- /** 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.
-
- @param[in] delimiter is the specified data.
-
- @param[out] nBytesBefore is the number of bytes before the
- specified data.
-
- @returns true if the delimiter is present in the buffer,
- false otherwise.
-
- */
- template<type U>
- bool checkForDelimiter(U &delimiter, size_t &nbytesBefore) {
- }
-
- /** 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);
- }
-
- }
-
- 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;
- }
-
- /// 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 (!isRunning()) {
- throw std::logic_error("No child process is running.");
- }
-
- 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>
-
- </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().
-
- */
- template<typename T, typename U>
- bool readDelimited(T &data, U &delimiter) {
-
- if (!isRunning()) {
- throw std::logic_error("No child process is running.");
- }
-
- data.clear();
-
- // Can be called in the user thread without locking the mutex.
- if (readBuffer.empty()) {
- return false;
- }
-
- 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();
-
- }
-
- /** 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 any description of the
- error.
-
- \returns true if an error occurred, false otherwise.
-
- */
- bool errorOccurred(std::string &errorDescription) const;
-
- /** 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 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:
-
- /// Initialize data members.
- void init();
-
- /** Spawn the child process.
-
- \throws runtime_error if an error occurs.
-
- */
- void run();
-
- /** 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.
-
- @param[in, out] dataPtr points to the memory that receives the
- data.
-
- @param[in] nBytes is the number of bytes to attempt to copy.
-
- @returns the number of bytes copied.
-
- */
- size_t read(char *const dataPtr, size_t nBytes);
-
- /// 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 and writerThread are to stop.
- bool stopFlag;
-
- /// True if both the reader and writer the writer threads are
- /// running, false otherwise.
- bool threadsAreRunning;
-
- /// 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 the child process has exited.
- bool childExited;
-
- /// 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
|