Import old svn repository.
This commit is contained in:
parent
f81b32b830
commit
432db441a8
111 changed files with 17147 additions and 37 deletions
53
src/liblw-support/IO/Device.h
Normal file
53
src/liblw-support/IO/Device.h
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
/* lw-support/src/lib/IO/Device.h
|
||||
*
|
||||
* (c)2005, Laurence Withers. Released under the GNU GPL. See file
|
||||
* COPYING for more information / terms of license.
|
||||
*/
|
||||
|
||||
namespace lw {
|
||||
|
||||
|
||||
|
||||
/*! \brief Base I/O device class.
|
||||
|
||||
This class is the base of the I/O device hierarchy. It specifies the
|
||||
common functionality, such as closing, reading and writing. It provides
|
||||
for both blocking and non-blocking I/O (which means that an 'open'
|
||||
function would have to open its file descriptor in non-blocking mode).
|
||||
|
||||
*/
|
||||
class IODevice : public IOInterface {
|
||||
protected:
|
||||
friend class EventManager;
|
||||
|
||||
/// Returns a file descriptor suitable for EventManager.
|
||||
virtual int getListenFd() const = 0;
|
||||
|
||||
public:
|
||||
/// Constructor. Does nothing.
|
||||
IODevice()
|
||||
{ }
|
||||
|
||||
/// Destructor. Calls close().
|
||||
virtual ~IODevice()
|
||||
{ }
|
||||
|
||||
|
||||
|
||||
/*! \brief Closes the device.
|
||||
|
||||
\throws SystemError if a system error occurs.
|
||||
|
||||
This call will close a device.
|
||||
|
||||
*/
|
||||
virtual void close() = 0;
|
||||
|
||||
// implemented virtuals
|
||||
virtual IOMode getIOMode() const
|
||||
{ return ioMode; }
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
||||
32
src/liblw-support/IO/Filter.cpp
Normal file
32
src/liblw-support/IO/Filter.cpp
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
/* lw-support/src/lib/IO/Filter.cpp
|
||||
*
|
||||
* (c)2005, Laurence Withers. Released under the GNU GPL. See file
|
||||
* COPYING for more information / terms of license.
|
||||
*/
|
||||
|
||||
namespace lw {
|
||||
|
||||
|
||||
|
||||
IOFilter::IOFilter(IOInterface* ioDev)
|
||||
: ioDev(ioDev)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
void IOFilter::sync()
|
||||
{
|
||||
try {
|
||||
flush();
|
||||
ioDev->sync();
|
||||
}
|
||||
catch(Exception& e) {
|
||||
e.chain(L"IOFilter::sync()");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
94
src/liblw-support/IO/Filter.h
Normal file
94
src/liblw-support/IO/Filter.h
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
/* lw-support/src/lib/IO/Filter.h
|
||||
*
|
||||
* (c)2005, Laurence Withers. Released under the GNU GPL. See file
|
||||
* COPYING for more information / terms of license.
|
||||
*/
|
||||
|
||||
namespace lw {
|
||||
|
||||
|
||||
|
||||
/*! \brief I/O filter base class.
|
||||
|
||||
The I/O filter class sits between the program and an underlying device
|
||||
(or another filter, since it is possible to create arbitrary chains of
|
||||
such filters). It can perform various transformations on the data. One
|
||||
example is a gzip filter, which compresses data before writing it and
|
||||
decompresses it when reading.
|
||||
|
||||
\warning Closing and reopening the underlying device may leave data in
|
||||
the filter's buffers. The solution is to call the reset()
|
||||
method (or delete the filter and create a new one).
|
||||
|
||||
*/
|
||||
class IOFilter : public IOInterface {
|
||||
protected:
|
||||
/// The source device.
|
||||
IOInterface* ioDev;
|
||||
|
||||
public:
|
||||
/// Constructor. Takes pointer to underlying device.
|
||||
IOFilter(IOInterface* ioDev);
|
||||
|
||||
/// Destructor. Flushes write cache.
|
||||
virtual ~IOFilter()
|
||||
{ }
|
||||
|
||||
|
||||
|
||||
/*! \brief Empty the filter's write buffers.
|
||||
|
||||
\throws SystemError if a system error occurs.
|
||||
\throws IOModeError if the file isn't open for writing.
|
||||
|
||||
This causes the filter to flush its write buffers to the underlying
|
||||
device. If the underlying device has its own write buffer, this
|
||||
function does not cause the underlying buffer to be flushed; that
|
||||
has to be done explicitly.
|
||||
|
||||
\sa sync()
|
||||
|
||||
*/
|
||||
virtual void flush() = 0;
|
||||
|
||||
|
||||
|
||||
/*! \brief Reset the filter.
|
||||
|
||||
This should only be called if you want to clear all buffer data and
|
||||
state information in the filter (e.g. if you have closed and
|
||||
reopened the underlying device). It's important to note that calling
|
||||
this will simply discard any data waiting to be read/written, so you
|
||||
should call flush() first.
|
||||
|
||||
*/
|
||||
virtual void reset() = 0;
|
||||
|
||||
|
||||
|
||||
// implemented virtuals
|
||||
virtual IOMode getIOMode() const
|
||||
{ return ioDev->getIOMode(); }
|
||||
|
||||
|
||||
|
||||
/*! \brief Ensure data is written to disk.
|
||||
|
||||
\throws SystemError if a system error occurs.
|
||||
\throws IOModeError if the file isn't open for writing.
|
||||
|
||||
This function will flush any output buffers to the underlying
|
||||
device, and will then propagate down the chain, until it reaches
|
||||
the eventual sink device (e.g. file or network connection), at which
|
||||
point it will call fdatasync() to ensure the data is physically
|
||||
written to disk/network/whatever.
|
||||
|
||||
\sa flush()
|
||||
|
||||
*/
|
||||
virtual void sync();
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
||||
183
src/liblw-support/IO/Interface.h
Normal file
183
src/liblw-support/IO/Interface.h
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
/* lw-support/src/lib/IO/Interface.h
|
||||
*
|
||||
* (c)2005, Laurence Withers. Released under the GNU GPL. See file
|
||||
* COPYING for more information / terms of license.
|
||||
*/
|
||||
|
||||
namespace lw {
|
||||
|
||||
|
||||
|
||||
/*! \brief I/O interface.
|
||||
|
||||
This class is the interface that any I/O device or filter will
|
||||
implement. It has both blocking and non-blocking functions.
|
||||
|
||||
*/
|
||||
class IOInterface {
|
||||
protected:
|
||||
/// The read/write mode of the device.
|
||||
IOMode ioMode;
|
||||
|
||||
public:
|
||||
/// Constructor. Initialises ioMode.
|
||||
IOInterface()
|
||||
: ioMode(IOClosed)
|
||||
{ }
|
||||
|
||||
|
||||
|
||||
/// Destructor -- does nothing.
|
||||
virtual ~IOInterface() { }
|
||||
|
||||
|
||||
|
||||
/// Gets the mode the device was opened in.
|
||||
virtual IOMode getIOMode() const = 0;
|
||||
|
||||
|
||||
|
||||
/*! \brief Read some data (non-blocking).
|
||||
|
||||
\param buf Buffer to read data into.
|
||||
\param amt Maximum amount of data to read (i.e. size of buffer).
|
||||
\throws SystemError if a system error occurs.
|
||||
\throws EndOfFile if end-of-file has been reached.
|
||||
\throws IOModeError if the file isn't open for reading.
|
||||
\returns Number of bytes actually read (which will be zero if no
|
||||
data is currently available).
|
||||
|
||||
Reads some data in a non-blocking manner. This call will return
|
||||
immediately and report the number of bytes read (which may well be
|
||||
less than the number of bytes requested). It will return zero if no
|
||||
data is available.
|
||||
|
||||
*/
|
||||
virtual size_t read(char* buf, size_t amt) = 0;
|
||||
|
||||
|
||||
|
||||
/*! \brief Write some data (non-blocking).
|
||||
|
||||
\param buf Data to write.
|
||||
\param amt Maximum amount of data to write.
|
||||
\throws SystemError if a system error occurs.
|
||||
\throws IOModeError if the file isn't open for writing.
|
||||
\returns Number of bytes actually written (will be zero if the
|
||||
device's output buffer is currently full).
|
||||
|
||||
Writes some data in a non-blocking manner. This call will return
|
||||
immediately and report the number of bytes written (which may well
|
||||
be less than the number of bytes requested). It will return zero if
|
||||
the device wasn't ready for writing.
|
||||
|
||||
*/
|
||||
virtual size_t write(const char* buf, size_t amt) = 0;
|
||||
|
||||
|
||||
|
||||
/*! \brief Wait for I/O.
|
||||
|
||||
\param read If \a true, returns when the device has input available.
|
||||
\param write If \a true, returns when you can output to the device.
|
||||
\param timeout Timeout period, in milliseconds. 0 means immediate;
|
||||
negative means forever.
|
||||
\throws IOTimeout if a timeout occurs.
|
||||
\throws SystemError if a system error occurs.
|
||||
\retval true if an exceptional condition occurred.
|
||||
\retval false if no exception condition occurred.
|
||||
|
||||
This function waits for the underlying device to become ready for
|
||||
the specified I/O operations, and returns when it is. It will
|
||||
return immediately if there is some exceptional condition, you can
|
||||
check for this by looking at the return value, which will be
|
||||
\a true if there is such a condition. Exceptional conditions
|
||||
include things such as out-of-band data and asynchronous errors.
|
||||
|
||||
*/
|
||||
virtual bool wait(bool read, bool write, int timeout) = 0;
|
||||
|
||||
|
||||
|
||||
/*! \brief Read some data (blocking) (full buffer).
|
||||
|
||||
\param buf Buffer to read data into.
|
||||
\param amt Maximum amount of data to read (i.e. size of buffer).
|
||||
\param timeout Timeout period, in milliseconds. 0 means immediate;
|
||||
negative means forever.
|
||||
\param timeoutMode The timeout mode.
|
||||
\throws SystemError if a system error occurs.
|
||||
\throws IOModeError if the file isn't open for reading.
|
||||
\throws EndOfFile if end-of-file has been reached.
|
||||
\throws IOTimeout if a timeout occurs.
|
||||
\returns Number of bytes actually read (always more than zero).
|
||||
|
||||
Reads some data, blocking if none is available. Various timeout
|
||||
options are available. By default, the entire buffer must be filled
|
||||
in the specified time period (but this is set to "forever" by
|
||||
default). Should the end of file be reached before the buffer is
|
||||
completely filled, an EndOfFile exception will be thrown; instead,
|
||||
you could use wait() and read() individually to get around this.
|
||||
|
||||
*/
|
||||
virtual size_t readBlock(char* buf, size_t amt, int timeout = -1,
|
||||
IOTimeoutMode timeoutMode = IOTimeoutHardFull) = 0;
|
||||
|
||||
|
||||
|
||||
/*! \brief Write some data (blocking).
|
||||
|
||||
\param buf Data to write.
|
||||
\param amt Amount of data to write.
|
||||
\param timeout Timeout period, in milliseconds. 0 means immediate;
|
||||
negative means forever.
|
||||
\param timeoutMode The timeout mode.
|
||||
\throws SystemError if a system error occurs.
|
||||
\throws IOModeError if the file isn't open for writing.
|
||||
\throws IOTimeout if a timeout occurs.
|
||||
\returns Number of bytes actually written (always more than zero).
|
||||
|
||||
Writes some data, blocking until it is sent. Various timeout
|
||||
options are available. By default, the entire buffer must be sent
|
||||
in the specified time period (but this is set to "forever" by
|
||||
default).
|
||||
|
||||
*/
|
||||
virtual size_t writeBlock(const char* buf, size_t amt, int timeout
|
||||
= -1, IOTimeoutMode timeoutMode = IOTimeoutHardFull) = 0;
|
||||
|
||||
|
||||
|
||||
/*! \brief Ensure data is written to disk.
|
||||
|
||||
\throws SystemError if a system error occurs.
|
||||
\throws IOModeError if the file isn't open for writing.
|
||||
|
||||
This function will flush any output buffers, then call fdatasync()
|
||||
on the underlying device, ensuring the data is written to disk (or
|
||||
network, etc.).
|
||||
|
||||
*/
|
||||
virtual void sync() = 0;
|
||||
|
||||
|
||||
|
||||
/*! \brief Check for asynchronous errors.
|
||||
|
||||
\throws SystemError if there are any asynchronous errors.
|
||||
|
||||
This function should be called to check for asynchronous errors.
|
||||
Examples of where you would use it are after a non-blocking
|
||||
connection has completed (socket will be marked as ready for
|
||||
reading/writing, so you need to check what's going on), and if
|
||||
you receive an error signal while waiting for events on the
|
||||
device, etc. Implementations of this function may do nothing if
|
||||
asynchronous errors can't occur (e.g. on standard disk files).
|
||||
|
||||
*/
|
||||
virtual void checkErrors() = 0;
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
||||
61
src/liblw-support/IO/Pipe.cpp
Normal file
61
src/liblw-support/IO/Pipe.cpp
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
/* lw-support/src/lib/IO/Pipe.cpp
|
||||
*
|
||||
* (c)2005, Laurence Withers. Released under the GNU GPL. See file
|
||||
* COPYING for more information / terms of license.
|
||||
*/
|
||||
|
||||
namespace lw {
|
||||
|
||||
|
||||
|
||||
Pipe::Pipe()
|
||||
: read(0), write(0)
|
||||
{
|
||||
int fds[2];
|
||||
if(TEMP_FAILURE_RETRY(::pipe(fds)))
|
||||
throw SystemError()
|
||||
.chain(L"Pipe::Pipe()").chain(L"pipe()");
|
||||
try {
|
||||
read = new EndPipe(this, fds[0], true);
|
||||
read->setNonBlocking();
|
||||
write = new EndPipe(this, fds[1], false);
|
||||
write->setNonBlocking();
|
||||
}
|
||||
catch(...) {
|
||||
delete read;
|
||||
delete write;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Pipe::~Pipe()
|
||||
{
|
||||
delete read;
|
||||
delete write;
|
||||
}
|
||||
|
||||
|
||||
|
||||
EndPipe::EndPipe(Pipe* myPipe, int fd, bool read)
|
||||
: myPipe(myPipe)
|
||||
{
|
||||
this->fd = fd;
|
||||
this->ioMode = read ? IORead : IOWrite;
|
||||
}
|
||||
|
||||
|
||||
|
||||
EndPipe::~EndPipe()
|
||||
{
|
||||
// unregister with the containing Pipe object
|
||||
if(ioMode == IORead) myPipe->read = 0;
|
||||
else myPipe->write = 0;
|
||||
|
||||
// closing happens automatically in IOPosixDevice's destructor.
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
70
src/liblw-support/IO/Pipe.h
Normal file
70
src/liblw-support/IO/Pipe.h
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
/* lw-support/src/lib/IO/Pipe.h
|
||||
*
|
||||
* (c)2005, Laurence Withers. Released under the GNU GPL. See file
|
||||
* COPYING for more information / terms of license.
|
||||
*/
|
||||
|
||||
namespace lw {
|
||||
|
||||
|
||||
|
||||
/*! \brief Pipe class container.
|
||||
|
||||
This class, when instantiated, creates a POSIX pipe device. This device
|
||||
has two ends (of type EndPipe); you can write to one of these ends, and
|
||||
read from the other.
|
||||
|
||||
This class just acts as a container for the two ends of the pipe.
|
||||
Deleting it will delete the two end pipes.
|
||||
|
||||
*/
|
||||
class Pipe : public Uncopyable {
|
||||
private:
|
||||
friend class EndPipe;
|
||||
|
||||
public:
|
||||
/*! \brief Constructor.
|
||||
|
||||
\throws lw::SystemError if an error occurs while creating the pipes.
|
||||
|
||||
The constructor sets up both ends of the pipe.
|
||||
|
||||
*/
|
||||
Pipe();
|
||||
|
||||
/// Destructor. Closes both ends of the pipe.
|
||||
~Pipe();
|
||||
|
||||
/// Reading end of the pipe.
|
||||
EndPipe* read;
|
||||
|
||||
/// Writing end of the pipe.
|
||||
EndPipe* write;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*! \brief One end of a pipe connection.
|
||||
|
||||
See the lw::Pipe class for a better description of POSIX pipes. This
|
||||
object has a private constructor, since it can only be created as part
|
||||
of an lw::Pipe container object.
|
||||
|
||||
The destructor will automatically unregister the object with its
|
||||
containing lw::Pipe, in order to avoid double deletion when the Pipe
|
||||
instance is deleted.
|
||||
|
||||
*/
|
||||
class EndPipe : public IOPosixDevice {
|
||||
private:
|
||||
friend class Pipe;
|
||||
Pipe* myPipe;
|
||||
EndPipe(Pipe* myPipe, int fd, bool read);
|
||||
|
||||
public:
|
||||
virtual ~EndPipe();
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
||||
379
src/liblw-support/IO/PosixDevice.cpp
Normal file
379
src/liblw-support/IO/PosixDevice.cpp
Normal file
|
|
@ -0,0 +1,379 @@
|
|||
/* lw-support/src/lib/IO/PosixDevice.cpp
|
||||
*
|
||||
* (c)2005, Laurence Withers. Released under the GNU GPL. See file
|
||||
* COPYING for more information / terms of license.
|
||||
*/
|
||||
|
||||
namespace lw {
|
||||
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
// This class is a helper class used to keep track of timeouts and
|
||||
// implement timeout behaviour for an I/O device.
|
||||
class IOCountdown {
|
||||
private:
|
||||
StopWatch started;
|
||||
IOInterface* ioInterface;
|
||||
std::wstring op;
|
||||
int timeout;
|
||||
IOTimeoutMode timeoutMode;
|
||||
size_t totalTransferred;
|
||||
|
||||
public:
|
||||
IOCountdown(IOInterface* ioInterface, const std::wstring& op, int timeout,
|
||||
IOTimeoutMode timeoutMode)
|
||||
: ioInterface(ioInterface), op(op), timeout(timeout),
|
||||
timeoutMode(timeoutMode), totalTransferred(0)
|
||||
{ }
|
||||
|
||||
// Call this when some data is transferred.
|
||||
void transferred(size_t amt)
|
||||
{
|
||||
// record amount transferred
|
||||
totalTransferred += amt;
|
||||
|
||||
// reset countdown timer
|
||||
if(amt && (timeoutMode == IOTimeoutSoftFull ||
|
||||
timeoutMode == IOTimeoutSoftPartial))
|
||||
{
|
||||
started.start();
|
||||
}
|
||||
}
|
||||
|
||||
// Call this when poll() returns a timeout. Returns no. of bytes
|
||||
// transferred, but throws an exception if specified by behaviour.
|
||||
size_t timedOut()
|
||||
{
|
||||
switch(timeoutMode) {
|
||||
case IOTimeoutHardPartial:
|
||||
case IOTimeoutSoftPartial:
|
||||
case IOTimeoutOneShot:
|
||||
if(totalTransferred) break;
|
||||
// fall through
|
||||
case IOTimeoutHardFull:
|
||||
case IOTimeoutSoftFull:
|
||||
throw IOTimeout(ioInterface, op, timeout,
|
||||
timeoutMode, totalTransferred);
|
||||
}
|
||||
return totalTransferred;
|
||||
}
|
||||
|
||||
// Call this when you need to update the timeout variable. It will
|
||||
// return false if the timeout has happened.
|
||||
bool update(int& remaining)
|
||||
{
|
||||
/* Special case for one-shot mode: after initial transfer has
|
||||
completed, we want to continue retrying until the I/O is
|
||||
drained; therefore, we set the timeout to zero.
|
||||
|
||||
FIXME: a stray signal could cause poll() to fail with EINTR,
|
||||
at which point update() will be called event though no I/O
|
||||
has yet occurred, possibly leading to a premature timeout.
|
||||
*/
|
||||
if(timeoutMode == IOTimeoutOneShot) return 0;
|
||||
|
||||
if(timeout < 0) {
|
||||
remaining = timeout;
|
||||
return true;
|
||||
}
|
||||
|
||||
remaining = timeout - started.elapsed().to_ms();
|
||||
return (remaining >= 0);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
IOPosixDevice::~IOPosixDevice()
|
||||
{
|
||||
if(fd >= 0) TEMP_FAILURE_RETRY(::close(fd));
|
||||
}
|
||||
|
||||
|
||||
|
||||
void IOPosixDevice::close()
|
||||
{
|
||||
if(fd != -1) {
|
||||
int ret = TEMP_FAILURE_RETRY(::close(fd));
|
||||
fd = -1;
|
||||
ioMode = IOClosed;
|
||||
|
||||
if(ret) {
|
||||
throw SystemError()
|
||||
.chain(L"close()")
|
||||
.chain(L"IOPosixDevice::close()");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
size_t IOPosixDevice::read(char* buf, size_t amt)
|
||||
{
|
||||
try {
|
||||
checkRead();
|
||||
|
||||
ssize_t ret = TEMP_FAILURE_RETRY(::read(fd, buf, amt));
|
||||
if(!ret) throw EndOfFile(this);
|
||||
if(ret == -1) {
|
||||
if(errno == EAGAIN) return 0;
|
||||
throw SystemError().chain(L"read()");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
catch(Exception& e) {
|
||||
e.chain(L"IOPosixDevice::read()");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
size_t IOPosixDevice::write(const char* buf, size_t amt)
|
||||
{
|
||||
try {
|
||||
checkWrite();
|
||||
|
||||
ssize_t ret = TEMP_FAILURE_RETRY(::write(fd, buf, amt));
|
||||
if(ret == -1) {
|
||||
if(errno == EAGAIN) return 0;
|
||||
throw SystemError().chain(L"write()");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
catch(Exception& e) {
|
||||
e.chain(L"IOPosixDevice::write()");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool IOPosixDevice::wait(bool read, bool write, int timeout)
|
||||
{
|
||||
struct pollfd ufd = { fd, POLLPRI | (read ? POLLIN : 0) | (write ? POLLOUT : 0), 0 };
|
||||
switch(poll(&ufd, 1, timeout)) {
|
||||
case -1:
|
||||
if(errno == EINTR) return false;
|
||||
throw SystemError().chain(L"IOPosixDevice::wait()").chain(L"poll()");
|
||||
|
||||
case 0:
|
||||
// timeout
|
||||
throw IOTimeout(this, L"wait()", timeout, IOTimeoutHardFull, 0);
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return (ufd.revents & (POLLERR | POLLPRI | POLLHUP | POLLNVAL));
|
||||
}
|
||||
|
||||
|
||||
|
||||
size_t IOPosixDevice::readBlock(char* buf, size_t amt, int timeout,
|
||||
IOTimeoutMode timeoutMode)
|
||||
{
|
||||
try {
|
||||
checkRead();
|
||||
|
||||
size_t remaining = amt;
|
||||
int timeout_remaining = timeout;
|
||||
struct pollfd ufd = { fd, POLLIN, 0 };
|
||||
IOCountdown countdown(this, L"readBlock()", timeout, timeoutMode);
|
||||
|
||||
while(true) {
|
||||
switch(poll(&ufd, 1, timeout_remaining)) {
|
||||
case -1:
|
||||
if(errno == EINTR) break;
|
||||
throw SystemError().chain(L"poll()");
|
||||
|
||||
case 0:
|
||||
// timeout
|
||||
return countdown.timedOut();
|
||||
|
||||
default:
|
||||
// perform read
|
||||
size_t res = read(buf, remaining);
|
||||
if( !(remaining -= res) ) return amt;
|
||||
buf += res;
|
||||
countdown.transferred(res);
|
||||
break;
|
||||
}
|
||||
|
||||
// update timeout
|
||||
if(!countdown.update(timeout_remaining))
|
||||
return countdown.timedOut();
|
||||
}
|
||||
}
|
||||
catch(Exception& e) {
|
||||
e.chain(L"IOPosixDevice::readBlock()");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
size_t IOPosixDevice::writeBlock(const char* buf, size_t amt, int timeout,
|
||||
IOTimeoutMode timeoutMode)
|
||||
{
|
||||
try {
|
||||
checkWrite();
|
||||
|
||||
size_t remaining = amt;
|
||||
int timeout_remaining = timeout;
|
||||
struct pollfd ufd = { fd, POLLOUT, 0 };
|
||||
IOCountdown countdown(this, L"writeBlock()", timeout, timeoutMode);
|
||||
|
||||
while(true) {
|
||||
switch(poll(&ufd, 1, timeout_remaining)) {
|
||||
case -1:
|
||||
if(errno == EINTR) break;
|
||||
throw SystemError().chain(L"poll()");
|
||||
|
||||
case 0:
|
||||
// timeout
|
||||
return countdown.timedOut();
|
||||
|
||||
default:
|
||||
// perform read
|
||||
size_t res = write(buf, remaining);
|
||||
if( !(remaining -= res) ) return amt;
|
||||
buf += res;
|
||||
countdown.transferred(res);
|
||||
break;
|
||||
}
|
||||
|
||||
// update timeout
|
||||
if(!countdown.update(timeout_remaining))
|
||||
return countdown.timedOut();
|
||||
}
|
||||
}
|
||||
catch(Exception& e) {
|
||||
e.chain(L"IOPosixDevice::writeBlock()");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void IOPosixDevice::sync()
|
||||
{
|
||||
try {
|
||||
checkWrite();
|
||||
}
|
||||
catch(Exception& e) {
|
||||
e.chain(L"IOPosixDevice::sync()");
|
||||
throw;
|
||||
}
|
||||
|
||||
if(TEMP_FAILURE_RETRY(::fdatasync(fd))) {
|
||||
throw SystemError()
|
||||
.chain(L"fdatasync()")
|
||||
.chain(L"IOPosixDevice::sync()");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void IOPosixDevice::setNonBlocking()
|
||||
{
|
||||
int flags = fcntl(fd, F_GETFL, 0);
|
||||
if(flags == -1) {
|
||||
throw SystemError()
|
||||
.chain(L"fcntl(..., F_GETFL, ...)")
|
||||
.chain(L"IOPosixDevice::setNonBlocking()");
|
||||
}
|
||||
if(flags & O_NONBLOCK) return;
|
||||
flags |= O_NONBLOCK;
|
||||
if(fcntl(fd, F_SETFL, flags)) {
|
||||
throw SystemError()
|
||||
.chain(L"fcntl(..., F_SETFL, ...)")
|
||||
.chain(L"IOPosixDevice::setNonBlocking()");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
int IOPosixDevice::ioModeFlags(IOMode mode)
|
||||
{
|
||||
switch(mode) {
|
||||
case IOClosed:
|
||||
throw IOModeError(this, L"open", IONone);
|
||||
|
||||
case IONone:
|
||||
return 0;
|
||||
|
||||
case IORead:
|
||||
return O_RDONLY;
|
||||
|
||||
case IOWrite:
|
||||
return O_WRONLY;
|
||||
|
||||
case IOReadWrite:
|
||||
return O_RDWR;
|
||||
}
|
||||
throw ProgramException();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void IOPosixDevice::checkRead() const
|
||||
{
|
||||
IOMode mode = getIOMode();
|
||||
if(mode == IORead || mode == IOReadWrite) return;
|
||||
throw IOModeError(const_cast<IOPosixDevice*>(this), L"read", IORead);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void IOPosixDevice::checkWrite() const
|
||||
{
|
||||
IOMode mode = getIOMode();
|
||||
if(mode == IOWrite || mode == IOReadWrite) return;
|
||||
throw IOModeError(const_cast<IOPosixDevice*>(this), L"write", IOWrite);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void IOPosixDevice::checkOpen(const std::wstring& op) const
|
||||
{
|
||||
IOMode mode = getIOMode();
|
||||
if(mode != IOClosed) return;
|
||||
throw IOModeError(const_cast<IOPosixDevice*>(this), op, IOWrite);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void IOPosixDevice::posixOpen(const char* path, IOMode mode)
|
||||
{
|
||||
fd = TEMP_FAILURE_RETRY(::open(path, ioModeFlags(mode)));
|
||||
if(fd == -1) {
|
||||
if(errno == ENOENT) throw FileNotFound(path);
|
||||
throw SystemError()
|
||||
.chain(L"open()")
|
||||
.chain(L"IOPosixDevice::posixOpen()");
|
||||
}
|
||||
try {
|
||||
setNonBlocking();
|
||||
}
|
||||
catch(Exception& e) {
|
||||
TEMP_FAILURE_RETRY(::close(fd));
|
||||
fd = -1;
|
||||
e.chain(L"IOPosixDevice::posixOpen()");
|
||||
throw;
|
||||
}
|
||||
ioMode = mode;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void IOPosixDevice::checkErrors()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
73
src/liblw-support/IO/PosixDevice.h
Normal file
73
src/liblw-support/IO/PosixDevice.h
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
/* lw-support/src/lib/IO/PosixDevice.h
|
||||
*
|
||||
* (c)2005, Laurence Withers. Released under the GNU GPL. See file
|
||||
* COPYING for more information / terms of license.
|
||||
*/
|
||||
|
||||
namespace lw {
|
||||
|
||||
|
||||
|
||||
/*! \brief POSIX I/O device class.
|
||||
|
||||
Many standard devices can be accessed through a POSIX file descriptor.
|
||||
This is the base class for all such devices. It provides implementations
|
||||
for reading, writing, etc.; but not for actually opening devices.
|
||||
|
||||
\todo Port readBlock() and writeBlock() over to use wait(), and deal
|
||||
with any exceptional conditions somehow.
|
||||
|
||||
*/
|
||||
class IOPosixDevice : public IODevice, public Uncopyable {
|
||||
protected:
|
||||
/// The POSIX file descriptor.
|
||||
int fd;
|
||||
|
||||
// implemented virtuals
|
||||
virtual int getListenFd() const
|
||||
{ return fd; }
|
||||
|
||||
/// Convenience function: sets the fd into non-blocking mode.
|
||||
void setNonBlocking();
|
||||
|
||||
/// Convenience function: converts enumeration value into GNU
|
||||
/// \c open() bitfield, throwing an exception if necessary.
|
||||
int ioModeFlags(IOMode mode);
|
||||
|
||||
/// Convenience function: POSIX open() wrapper, throws exception.
|
||||
void posixOpen(const char* path, IOMode mode);
|
||||
|
||||
/// Convenience function: check that we can read or throw exception.
|
||||
void checkRead() const;
|
||||
|
||||
/// Convenience function: check that we can write or throw exception.
|
||||
void checkWrite() const;
|
||||
|
||||
/// Convenience function: check device is open or throw exception.
|
||||
void checkOpen(const std::wstring& op) const;
|
||||
|
||||
public:
|
||||
/// Constructor. Does nothing.
|
||||
IOPosixDevice()
|
||||
: fd(-1)
|
||||
{ }
|
||||
|
||||
/// Destructor. Closes device.
|
||||
virtual ~IOPosixDevice();
|
||||
|
||||
// implemented virtuals
|
||||
virtual void close();
|
||||
virtual size_t read(char* buf, size_t amt);
|
||||
virtual size_t write(const char* buf, size_t amt);
|
||||
virtual bool wait(bool read, bool write, int timeout);
|
||||
virtual size_t readBlock(char* buf, size_t amt, int timeout = -1,
|
||||
IOTimeoutMode timeoutMode = IOTimeoutHardFull);
|
||||
virtual size_t writeBlock(const char* buf, size_t amt, int timeout
|
||||
= -1, IOTimeoutMode timeoutMode = IOTimeoutHardFull);
|
||||
virtual void sync();
|
||||
virtual void checkErrors();
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
||||
76
src/liblw-support/IO/SerialPort.cpp
Normal file
76
src/liblw-support/IO/SerialPort.cpp
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
/* lw-support/src/lib/IO/SerialPort.cpp
|
||||
*
|
||||
* (c)2005, Laurence Withers. Released under the GNU GPL. See file
|
||||
* COPYING for more information / terms of license.
|
||||
*/
|
||||
|
||||
namespace lw {
|
||||
|
||||
|
||||
|
||||
void IOSerialPort::open(IOMode mode, const char* path, int baud, bool twoStopBits)
|
||||
{
|
||||
if(fd != -1) close();
|
||||
|
||||
try {
|
||||
posixOpen(path, mode);
|
||||
setOptions(baud, twoStopBits);
|
||||
}
|
||||
catch(Exception& e) {
|
||||
e.chain(L"IOSerialPort::open()");
|
||||
close();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void IOSerialPort::setOptions(int baud, bool twoStopBits)
|
||||
{
|
||||
// read current characteristics
|
||||
struct termios term;
|
||||
if(tcgetattr(fd, &term) < 0) {
|
||||
throw SystemError()
|
||||
.chain(L"tcgetattr()")
|
||||
.chain(L"IOSerialPort::setOptions()");
|
||||
}
|
||||
|
||||
// set 8N1 mode
|
||||
term.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR
|
||||
| ICRNL | IXON | IXOFF);
|
||||
term.c_oflag &= ~OPOST;
|
||||
term.c_cflag &= ~(PARENB | CSIZE | CSTOPB);
|
||||
term.c_cflag |= CS8;
|
||||
term.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
|
||||
|
||||
// set baud rate and stop bits
|
||||
if(twoStopBits) term.c_cflag |= CSTOPB;
|
||||
if(cfsetspeed(&term, baud)) {
|
||||
throw SystemError()
|
||||
.chain(L"cfsetspeed()")
|
||||
.chain(L"IOSerialPort::setOptions()");
|
||||
}
|
||||
|
||||
// set attributes
|
||||
if(tcsetattr(fd, TCSAFLUSH, &term)) {
|
||||
throw SystemError()
|
||||
.chain(L"tcsetattr()")
|
||||
.chain(L"IOSerialPort::setOptions()");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
void IOSerialPort::discardInput()
|
||||
{
|
||||
if(TEMP_FAILURE_RETRY(tcflush(fd, TCIFLUSH))) {
|
||||
throw SystemError()
|
||||
.chain(L"tcflush()")
|
||||
.chain(L"IOSerialPort::discardInput()");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
70
src/liblw-support/IO/SerialPort.h
Normal file
70
src/liblw-support/IO/SerialPort.h
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
/* lw-support/src/lib/IO/SerialPort.h
|
||||
*
|
||||
* (c)2005, Laurence Withers. Released under the GNU GPL. See file
|
||||
* COPYING for more information / terms of license.
|
||||
*/
|
||||
|
||||
namespace lw {
|
||||
|
||||
|
||||
|
||||
/*! \brief Serial port I/O device.
|
||||
|
||||
This class allows you to control a serial (RS232) port or equivalent
|
||||
device. It provides control over the baud rate and whether you want to
|
||||
use one stop bit (the standard) or two (useful for communicating with
|
||||
embedded devices when the baud rate generators don't exactly match). It
|
||||
does not provide support for parity, flow control or other than 8 data
|
||||
bits.
|
||||
|
||||
*/
|
||||
class IOSerialPort : public IOPosixDevice {
|
||||
public:
|
||||
/// Constructor (doesn't open any device).
|
||||
IOSerialPort()
|
||||
{ }
|
||||
|
||||
|
||||
|
||||
/*! \brief Open or reopen device.
|
||||
|
||||
\param mode The mode in which the device should be opened.
|
||||
\param path Path to the device node to open.
|
||||
\param baud The baud rate you want.
|
||||
\param twoStopBits \a true if you want two stop bits; \a false if
|
||||
you don't.
|
||||
\throws IOModeError if you pass lw::IOClosed as the mode.
|
||||
\throws SystemError if the device couldn't be opened or if the
|
||||
options cannot be set.
|
||||
|
||||
Opens the device, setting the desired baud rate and number of stop
|
||||
bits. If a device is already open, it is closed first.
|
||||
|
||||
*/
|
||||
void open(IOMode mode, const char* path, int baud,
|
||||
bool twoStopBits = false);
|
||||
|
||||
|
||||
|
||||
/*! \brief Change device options (baud rate etc.).
|
||||
|
||||
\param baud The new baud rate.
|
||||
\param twoStopBits \a true if you want two stop bits; \a false if
|
||||
you don't.
|
||||
\throws IOModeError if the device is closed.
|
||||
\throws SystemError if the options cannot be set.
|
||||
|
||||
Changes the baud rate and number of stop bits.
|
||||
|
||||
*/
|
||||
void setOptions(int baud, bool twoStopBits = false);
|
||||
|
||||
|
||||
|
||||
/// Discard any input waiting to be read.
|
||||
void discardInput();
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
||||
119
src/liblw-support/IO/Util/WriteTask.cpp
Normal file
119
src/liblw-support/IO/Util/WriteTask.cpp
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
/* lw-support/src/lib/IO/Util/WriteTask.cpp
|
||||
*
|
||||
* (c)2005, Laurence Withers. Released under the GNU GPL. See file
|
||||
* COPYING for more information / terms of license.
|
||||
*/
|
||||
|
||||
namespace lw {
|
||||
|
||||
|
||||
|
||||
IOWriteTask::IOWriteTask(lw::EventManager& eventManager, IODevice* ioDev, int timeoutInactive,
|
||||
int timeoutOverall, CompletionCallback* completion,
|
||||
const char* data, size_t amt, bool copy)
|
||||
: CompletionTask(completion), eventManager(eventManager), ioDev(ioDev),
|
||||
timeoutInactive(timeoutInactive), timeoutOverall(timeoutOverall), data(0), wr(0), amt(amt),
|
||||
copy(copy)
|
||||
{
|
||||
try {
|
||||
NullPointer::check(data, L"data");
|
||||
NullPointer::check(ioDev, L"ioDev");
|
||||
|
||||
eventManager.registerDevice(ioDev, this, lw::IOEventHUP | lw::IOEventWrite
|
||||
| lw::IOEventError);
|
||||
#if 0
|
||||
timerKey2 = eventManager.registerTimer(this, lw::ElapsedTime(0));
|
||||
#endif
|
||||
|
||||
// copy data if necessary
|
||||
if(copy) {
|
||||
this->data = new char[amt];
|
||||
memcpy(this->data, data, amt);
|
||||
} else {
|
||||
this->data = (char*)data;
|
||||
}
|
||||
wr = this->data;
|
||||
|
||||
// set timeouts if necessary
|
||||
if(timeoutInactive >= 0)
|
||||
timerKey0 = eventManager.registerTimer(this, lw::ElapsedTime::from_ms(timeoutInactive));
|
||||
if(timeoutOverall >= 0)
|
||||
timerKey1 = eventManager.registerTimer(this, lw::ElapsedTime::from_ms(timeoutOverall));
|
||||
}
|
||||
catch(lw::Exception& e) {
|
||||
if(copy) delete [] this->data;
|
||||
e.chain(L"IOWriteTask::IOWriteTask()");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
IOWriteTask::~IOWriteTask()
|
||||
{
|
||||
if(this->timeoutInactive >= 0) eventManager.ignoreTimer(timerKey0);
|
||||
if(this->timeoutOverall >= 0) eventManager.ignoreTimer(timerKey1);
|
||||
delete ioDev;
|
||||
// if(copy) delete [] data;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void IOWriteTask::taskAbort(const lw::Exception& e)
|
||||
{
|
||||
taskAborted(e);
|
||||
delete this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool IOWriteTask::ioReady(uint32_t flags)
|
||||
{
|
||||
try {
|
||||
if(flags & lw::IOEventError) ioDev->checkErrors();
|
||||
if(flags & lw::IOEventHUP) throw lw::Exception(L"Remote device closed connection.");
|
||||
if(flags & lw::IOEventWrite) tryWrite();
|
||||
}
|
||||
catch(lw::Exception& e) {
|
||||
taskAbort(e);
|
||||
delete this;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void IOWriteTask::tryWrite()
|
||||
{
|
||||
while(amt) {
|
||||
size_t w = ioDev->write(wr, amt);
|
||||
if(!w) return;
|
||||
|
||||
wr += w;
|
||||
amt -= w;
|
||||
|
||||
if(timeoutInactive >= 0) {
|
||||
eventManager.ignoreTimer(timerKey0);
|
||||
timerKey0 = eventManager.registerTimer(this, lw::ElapsedTime::from_ms(timeoutInactive), 0);
|
||||
}
|
||||
}
|
||||
|
||||
// done!
|
||||
taskCompleted();
|
||||
delete this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool IOWriteTask::timer(Key)
|
||||
{
|
||||
// must be a timeout
|
||||
taskAbort(lw::Exception(L"WriteTask timeout."));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
81
src/liblw-support/IO/Util/WriteTask.h
Normal file
81
src/liblw-support/IO/Util/WriteTask.h
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
/* lw-support/src/lib/IO/Util/WriteTask.h
|
||||
*
|
||||
* (c)2005, Laurence Withers. Released under the GNU GPL. See file
|
||||
* COPYING for more information / terms of license.
|
||||
*/
|
||||
|
||||
namespace lw {
|
||||
|
||||
|
||||
|
||||
/*! \brief Class which writes some data to an I/O device and then closes it.
|
||||
|
||||
Instances of this class will write data to an I/O device, using non-blocking I/O, and (on
|
||||
destruction) will close the device and delete it. It has options for timeouts and deals correctly
|
||||
with errors. On task completion, the object will delete itself.
|
||||
|
||||
A typical usage example would be to accept a TCP connection, instantiate an IOWriteTask (with a
|
||||
suitable message and timeout, pointed at the TCP connection, connected to a CompletionList object),
|
||||
register the task with the CompletionList, and then simply process events as usual, forgetting about
|
||||
the TCP connection and the IOWriteTask instance.
|
||||
|
||||
*/
|
||||
class IOWriteTask : public CompletionTask,
|
||||
private EventCallbackIO,
|
||||
private EventCallbackTimer
|
||||
{
|
||||
private:
|
||||
lw::EventManager& eventManager;
|
||||
IOInterface* ioDev;
|
||||
int timeoutInactive, timeoutOverall;
|
||||
char* data, * wr;
|
||||
size_t amt;
|
||||
bool copy;
|
||||
Key timerKey0, timerKey1;
|
||||
|
||||
void tryWrite();
|
||||
|
||||
// from EventCallbackIO
|
||||
virtual bool ioReady(uint32_t flags);
|
||||
|
||||
// from EventCallbackTimer
|
||||
virtual bool timer(Key key);
|
||||
|
||||
public:
|
||||
/*! \brief Constructor. Sets up the task.
|
||||
|
||||
\param eventManager The event manager object to use.
|
||||
\param ioDev The I/O device in question.
|
||||
\param timeoutInactive An inactivity timeout period, in milliseconds. -1 to disable.
|
||||
\param timeoutOverall An overall timeout period, in milliseconds. -1 to disable.
|
||||
\param completion The object which will be notified about completion. Can be 0.
|
||||
\param data Pointer to the block of data to write.
|
||||
\param amt Number of bytes of data to write.
|
||||
\param copy Pass \c true if you want the data to be copied, or \c false if you guarantee its
|
||||
persistence until the task is completed.
|
||||
|
||||
\throws NullPointer if \a ioDev or \a data is 0.
|
||||
\throws SystemError if a system call fails.
|
||||
|
||||
Begins the process. Registers the device with the event manager, attempts to write some data
|
||||
(these can result in system errors). On completion, the object will delete itself.
|
||||
|
||||
*/
|
||||
IOWriteTask(lw::EventManager& eventManager, IODevice* ioDev, int timeoutInactive,
|
||||
int timeoutOverall, CompletionCallback* completion,
|
||||
const char* data, size_t amt, bool copy);
|
||||
|
||||
|
||||
|
||||
/// Destructor. Closes and deletes I/O device.
|
||||
virtual ~IOWriteTask();
|
||||
|
||||
|
||||
|
||||
/// Aborts task, closing and deleting the I/O device and the instance.
|
||||
virtual void taskAbort(const lw::Exception& e);
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue