Import old svn repository.

This commit is contained in:
Laurence Withers 2006-08-25 00:09:46 +01:00
commit 432db441a8
111 changed files with 17147 additions and 37 deletions

View 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; }
};
}

View 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;
}
}
}

View 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();
};
}

View 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;
};
}

View 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.
}
}

View 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();
};
}

View 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()
{
}
}

View 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();
};
}

View 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()");
}
}
}

View 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();
};
}

View 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;
}
}

View 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);
};
}