|
|
|
|
|
|
|
|
This method gets the specified number of bytes from the |
|
|
This method gets the specified number of bytes from the |
|
|
buffer, and erases those bytes from the buffer. |
|
|
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 |
|
|
@param[out] buf receives the data. The contents of buf are |
|
|
replaced with the data in the circular buffer. |
|
|
replaced with the data in the circular buffer. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
a value of zero for nBytes gets and erases all the data. |
|
|
a value of zero for nBytes gets and erases all the data. |
|
|
|
|
|
|
|
|
*/ |
|
|
*/ |
|
|
void getAndErase(std::string &buf, size_t nBytes = 0); |
|
|
|
|
|
|
|
|
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: |
|
|
private: |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** All-or-nothing non-blocking queue write request to the child. |
|
|
/** All-or-nothing non-blocking queue write request to the child. |
|
|
|
|
|
|
|
|
This methods attempts to queue a write request consisting of |
|
|
This methods attempts to queue a write request consisting of |
|
|
the entire contents of the string. |
|
|
|
|
|
|
|
|
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. |
|
|
@param[in] data is the data to queue. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
otherwise. |
|
|
otherwise. |
|
|
|
|
|
|
|
|
*/ |
|
|
*/ |
|
|
bool write(std::string const &data); |
|
|
|
|
|
|
|
|
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. |
|
|
/** Non-blocking queue write request to the child. |
|
|
|
|
|
|
|
|
This methods attempts to queue a write request consisting of |
|
|
This methods attempts to queue a write request consisting of |
|
|
as much as possible of the contents of the string. |
|
|
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 |
|
|
@param[in, out] data on input is the data to queue. On |
|
|
output, data contains the data that was not queued. |
|
|
output, data contains the data that was not queued. |
|
|
|
|
|
|
|
|
@returns the number of bytes queued. |
|
|
@returns the number of bytes queued. |
|
|
|
|
|
|
|
|
*/ |
|
|
*/ |
|
|
size_t writeAndShrink(std::string &data); |
|
|
|
|
|
|
|
|
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. |
|
|
/** Check if all queued data was transmitted. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
the child. The data that is provided is erased from the input |
|
|
the child. The data that is provided is erased from the input |
|
|
buffer. |
|
|
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[out] data contains the copied data. |
|
|
|
|
|
|
|
|
@param[in] nBytes is the number of bytes to attempt to copy. |
|
|
@param[in] nBytes is the number of bytes to attempt to copy. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@returns the number of bytes copied. |
|
|
@returns the number of bytes copied. |
|
|
|
|
|
|
|
|
|
|
|
@see CircularBuffer::getAndErase(). |
|
|
|
|
|
|
|
|
*/ |
|
|
*/ |
|
|
size_t read(std::string &data, size_t nBytes = 0); |
|
|
|
|
|
|
|
|
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(); |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
/** Check whether the child process is running. |
|
|
/** Check whether the child process is running. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
*/ |
|
|
void run(); |
|
|
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. |
|
|
/// Reader thread object. |
|
|
std::thread readerThread; |
|
|
std::thread readerThread; |
|
|
|
|
|
|