pipes.h
Go to the documentation of this file.
/* Copyright (C) 1999 to 2004, and 2009 to 2011 Chris Vine
The library comprised in this file or of which this file is part is
distributed by Chris Vine under the GNU Lesser General Public
License as follows:
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
as published by the Free Software Foundation; either version 2.1 of
the License, or (at your option) any later version.
This library 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
Lesser General Public License, version 2.1, for more details.
You should have received a copy of the GNU Lesser General Public
License, version 2.1, along with this library (see the file LGPL.TXT
which came with this source code package in the c++-gtk-utils
sub-directory); if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef CGU_PIPES_H
#define CGU_PIPES_H
#include <unistd.h>
#include <exception>
#include <c++-gtk-utils/cgu_config.h>
namespace Cgu {
/**
* @class PipeFifo pipes.h c++-gtk-utils/pipes.h
* @brief A wrapper for unix anonymous pipes.
* @sa SyncPipe
*
* This class provides a simplified front end for anonymous fifo pipes
* (as created by the pipe() system call). The constructor of the
* class may be passed an enumerator to indicate whether the pipe is
* to read in non-blocking mode.
*
* With the write(const char*, int) method, taking an array and int
* arguments, the value of int (indicating the number of chars in the
* array to be written) should usually be less than PIPE_BUF, as POSIX
* write() guarantees that if it is less than PIPE_BUF, either all
* will be written or, if the pipe is too full, none will be written.
* This prevents data interleaving if a number of independent writes
* to the fifo are made. The same is true of the write (const char*)
* method taking a string - if the string is longer than PIPE_BUF in
* size, data interleaving may occur.
*
* The pipe is set to read in non-blocking mode if the constructor of
* PipeFifo, or PipeFifo::open(), is passed the value
* PipeFifo::non_block. If constructed in this way, the pipe can also
* be set to write in non-block mode (eg to minimise the impact on
* another program running in a child process to which the child
* process has exec()ed when monitoring its stdin or stderr) by
* calling make_write_non_block(). However use this very sparingly --
* when set in this way write() will always try to write something.
* The atomic guarantees mentioned in the preceding paragraph do not
* apply and data can be interleaved or lost.
*
* PIPE_BUF is defined in limits.h, and is at least 512 bytes, and
* usually 4096 bytes, but may be calculated from other files included
* by limits.h.
*
* Where a pipe is used to communicate between parent and child after
* a call to fork(), the pipe will normally be used unidirectionally
* unless guards or semaphores are used to prevent a process reading
* data intended for the other. However, because each process
* inherits its own duplicate of the file descriptors, this cannot be
* enforced without closing the read or write file descriptor for the
* process for which the reading or writing is to be prohibited. This
* can be done by the process concerned calling the methods
* PipeFifo::make_writeonly() or PipeFifo::make_readonly() after the
* fork. If an attempt is made to read or write to a descriptor
* closed in this way, the PipeFifo::read() or PipeFifo::write()
* method will ensure that no read or write will take place, and
* instead -2 will be returned.
*
* The methods PipeFifo::connect_to_stdin(),
* PipeFifo::connect_to_stdout() and PipeFifo::connect_to_stderr() are
* available to be used in the child process before it exec()s another
* program so as to connect the pipe to that program's stdin, stdout
* or stderr. PipeFifo::connect_to_stdin() cannot be used by a
* process after that process has called PipeFifo::make_writeonly(),
* and PipeFifo::connect_to_stdout() and PipeFifo::connect_to_stderr()
* cannot be used after the process has called
* PipeFifo::make_readonly(), nor can a pipe in any one process be
* connected to more than one of stdin, stdout or stderr. If that is
* attempted the methods will return -2. Furthermore, they should
* only be used after the process creating the pipe has forked. If
* the connection to stdin, stdout or stderr is to be made before the
* fork, this must be done by hand using dup2(),
* PipeFifo::get_write_fd()/ PipeFifo::get_read_fd() and
* PipeFifo::make_read_only()/ PipeFifo::make_write_only().
*
* If PipeFifo::connect_to_stdin() is called by a method,
* PipeFifo::make_readonly() will also be called, and if
* PipeFifo::connect_to_stdout() or PipeFifo::connect_to_stderr() are
* called, PipeFifo::make_writeonly() will also be called. This will
* isolate the use of the pipe by the child process to stdin, stdout
* or stderr, as appropriate.
*
* It uses no static members, so is thread safe as between different
* objects, but its methods are not thread safe as regards any one
* object in the sense that the read() and write() methods check the
* value of read_fd and write_fd respectively (and get_read_fd() and
* get_write_fd() return them), and make_writeonly(), make_readonly(),
* close(), connect_to_stdin(), open(), connect_to_stdout() and
* connect_to_stderr() change those values. Likewise the read() and
* write() methods access read_blocking_mode and write_blocking_mode
* respectively, and these are changed by open() and
* make_write_non_block(). Provided there is no concurrent use of
* read(), write(), get_read_fd() or get_write_fd() in one thread with
* a call of a method which changes read_fd, write_fd,
* read_blocking_mode or write_blocking_mode as described above in
* another thread then no mutex is required to ensure thread safety.
*
* All the read() and write() methods check for an interruption of the
* system call from a signal (EINTR is checked), and will continue to
* read() or write() where necessary. Users do not need to check
* EINTR themselves. Where the write file descriptor is flagged as
* blocking (that is, where PipeFifo::make_write_non_block() has not
* been called), then in the absence of some other error, everything
* passed to PipeFifo::write() will be written (but as mentioned above
* there may be data interleaving if this is greater than PIPE_BUF in
* size). Where the write file descriptor is flagged as non-blocking,
* then the result of Unix write is returned, and less bytes than
* those passed to PipeFifo::write() may have been written, or -1 may
* be returned with errno set to EAGAIN - it is for the user to check
* this.
*
* If you are using pipes to write and read between processes, you
* will probably want to call sigaction() to cause the SIGPIPE signal
* to be ignored. SIGPIPE has little use in most programming: if
* SIGPIPE is ignored, and an attempt is made to write to a pipe which
* has been closed at the read end, then the write will return -1 and
* errno will be set to EPIPE.
*/
struct PipeError: public std::exception {
virtual const char* what() const throw() {return "PipeError: error opening pipe\n";}
};
class PipeFifo {
public:
enum Fifo_mode{block, non_block};
private:
int read_fd;
int write_fd;
Fifo_mode read_blocking_mode;
Fifo_mode write_blocking_mode;
public:
/**
* This class cannot be copied. The copy constructor is deleted.
*/
PipeFifo(const PipeFifo&) = delete;
/**
* This class cannot be copied. The assignment operator is deleted.
*/
PipeFifo& operator=(const PipeFifo&) = delete;
/**
* Opens read and write file descriptors for the pipe. If the pipe is
* already open, it will close the existing file descriptors. This
* function call is thread-safe to the extent that all the system
* calls made in its implementation are async-signal-safe, so it may
* safely be called in a multi-threaded program after a fork() and
* before an exec(). However, the manipulation of the file
* descriptors and related variables is not protected by a mutex, so
* if different threads in the same process are to manipulate the same
* pipe object with this method, close(), connect_to_stdin(),
* connect_to_stdout() or connect_to_stderr() (which manipulate both
* descriptors), make_readonly() or make_write_non_block() (which
* manipulate the write file descriptor), or make_writeonly() (which
* manipulates the read file descriptor), or read the value of the
* file descriptor with get_read_fd() or read() (which access the read
* file descriptor) or get_write_fd() or write() (which access the
* write file descriptor) concurrently with such a manipulation, then
* the user should provide appropriate synchronisation.
* @param mode The mode in which the read file descriptor of the pipe
* is to be opened - either Cgu::PipeFifo::block or
* Cgu::PipeFifo::non_block.
* @exception Cgu::PipeError This exception is thrown if the opening
* of the pipe fails. No other exceptions will be thrown.
*/
void open(Fifo_mode mode);
/**
* Closes the read and write file descriptors of the pipe (if they are
* open). This function call is thread-safe to the extent that all
* the system calls made in its implementation are async-signal-safe,
* so it may safely be called in a multi-threaded program after a
* fork() and before an exec(). However, the manipulation of the file
* descriptors and related variables is not protected by a mutex, so
* if different threads in the same process are to manipulate the same
* pipe object with this method, open(), connect_to_stdin(),
* connect_to_stdout() or connect_to_stderr() (which manipulate both
* descriptors), make_readonly() or make_write_non_block() (which
* manipulate the write file descriptor), or make_writeonly() (which
* manipulates the read file descriptor), or read the value of the
* file descriptor with get_read_fd() or read() (which access the read
* file descriptor) or get_write_fd() or write() (which access the
* write file descriptor) concurrently with such a manipulation, then
* the user should provide appropriate synchronisation. No exceptions
* will be thrown.
*/
void close();
/**
* Reads up to max_num characters from the pipe. This read does not
* carry on blocking until the read request is met: unless there is an
* interrupt, it just calls POSIX read() once and takes what is
* available. If a specific number of characters is required, the
* user should provide an appropriate do/while loop. It does not
* throw. Any reasonable POSIX implementation will do an atomic read
* as between threads if max_num is PIPE_BUF or less in size, but the
* POSIX standard only requires such atomic reads as between processes
* rather than as between threads. The user does not need to check
* for EINTR and an interrupt: this method deals with that
* automatically. All the system calls made in its implementation are
* async-signal-safe, and it is re-entrant subject to errno being
* saved. This method does not throw.
* @param buf A buffer into which the characters will be placed.
* @param max_num The maximum number of characters to be read
* (normally the size of the buffer).
* @return -2 if the read file descriptor is invalid, otherwise the
* result of unix read() - that is, the number of characters read, or
* 0 if end of file is reached (ie the write descriptor has been
* closed), or -1 if there has been an error (but EINTR never returns
* as an error as the implementation handles that). In the event of
* an error, errno is set.
*/
ssize_t read(char* buf, size_t max_num);
/**
* Extracts a character from the pipe. This method does not throw.
* All the system calls made in its implementation are
* async-signal-safe and it is re-entrant subject to errno being
* saved. It is thread safe.
* @return The character at the front of the pipe if available,
* otherwise -2 if the read file descriptor is invalid, 0 if end of
* file is reached (ie the write descriptor has been closed), or -1 if
* there has been an error (but EINTR never returns as an error as the
* implementation handles that). In the event of an error, errno is
* set.
*/
int read();
/**
* Write a NUL terminated C string to the pipe, excluding the
* terminating NUL character. If the write file descriptor is set as
* blocking (the default) then this method will carry on blocking
* until everything is sent, even if it exceeds PIPE_BUF in size. Any
* reasonable POSIX implementation will do an atomic write (with no
* interleaving) as between threads if the number of characters in the
* string is PIPE_BUF or less in size, but the POSIX standard only
* requires such atomic writes as between processes rather than as
* between threads. All the system calls made in its implementation
* are async-signal-safe and it is re-entrant subject to errno being
* saved. This method does not throw.
* @param str The string to be written.
* @return -2 if write file descriptor invalid, otherwise it returns
* the number of bytes written or -1 if there has been an error (but
* EINTR never returns as an error as the implementation handles
* that). In the event of an error, errno is set.
*/
ssize_t write(const char* str);
/**
* Write a buffer of characters to the pipe. If the write file
* descriptor is set as blocking (the default) then this method will
* carry on blocking until everything is sent, even if it exceeds
* PIPE_BUF in size. Any reasonable POSIX implementation will do an
* atomic write (with no interleaving) as between threads if num is
* PIPE_BUF or less in size, but the POSIX standard only requires such
* atomic writes as between processes rather than as between threads.
* All the system calls made in its implementation are
* async-signal-safe and it is re-entrant subject to errno being
* saved. This method does not throw.
* @param buf The buffer to be written.
* @param num The number of characters in the buffer.
* @return -2 if write file descriptor invalid, otherwise it returns
* the number of bytes written or -1 if there has been an error (but
* EINTR never returns as an error as the implementation handles
* that). In the event of an error, errno is set.
*/
ssize_t write(const char* buf, size_t num);
/**
* Write a character to the pipe. All the system calls made in its
* implementation are async-signal-safe and it is re-entrant subject
* to errno being saved. This method does not throw. It is thread
* safe.
* @param item The character to be written.
* @return 1 if the character was written, -2 if the write file
* descriptor is invalid, or -1 if there has been an error (but EINTR
* never returns as an error as the implementation handles that). In
* the event of an error, errno is set.
*/
int write(char item) {return write(&item, 1);}
/**
* Make the pipe only available for writing in the calling process, by
* closing the read file descriptor. This function call is thread
* safe to the extent that all the system calls made in its
* implementation are async-signal-safe, so it may safely be called in
* a multi-threaded program after a fork() and before an exec().
* However, the manipulation of the file descriptor and related
* variables is not protected by a mutex, so if different threads in
* the same process are to manipulate the same pipe object with
* open(), close(), connect_to_stdin(), connect_to_stdout() or
* connect_to_stderr() (which manipulate both descriptors), or this
* method (which manipulates the read file descriptor), or read the
* value of the read file descriptor with get_read_fd() or read()
* concurrently with such a manipulation, then the user should provide
* appropriate synchronisation. No exceptions will be thrown.
*/
void make_writeonly();
/**
* Make the pipe only available for reading in the calling process, by
* closing the write file descriptor. This function call is
* thread-safe to the extent that all the system calls made in its
* implementation are async-signal-safe, so it may safely be called in
* a multi-threaded program after a fork() and before an exec().
* However, the manipulation of the file descriptor and related
* variables is not protected by a mutex, so if different threads in
* the same process are to manipulate the same pipe object with
* open(), close(), connect_to_stdin(), connect_to_stdout() or
* connect_to_stderr() (which manipulate both descriptors), or this
* method or make_write_non_block() (which manipulate the write file
* descriptor), or read the value of the write file descriptor with
* get_write_fd() or write() concurrently with such a manipulation,
* then the user should provide appropriate synchronisation. No
* exceptions will be thrown.
*/
void make_readonly();
/**
* Makes the write file descriptor non-blocking. This is only very
* rarely needed. If the write file descriptor is set non-blocking,
* then there are no atomic write guarantees. This function call is
* thread safe to the extent that all the system calls made in its
* implementation are async-signal-safe, so it may safely be called in
* a multi-threaded program after a fork() and before an exec().
* However, the manipulation of the file descriptor and related
* variables is not protected by a mutex, so if different threads in
* the same process are to manipulate the same pipe object with
* open(), close(), connect_to_stdin(), connect_to_stdout() or
* connect_to_stderr() (which manipulate both descriptors), or this
* method or make_readonly() (which manipulate the write file
* descriptor), or read the value of the write file descriptor with
* get_write_fd() or write() concurrently with such a manipulation,
* then the user should provide appropriate synchronisation. No
* exceptions will be thrown.
* @return 0 if the write file descriptor is open, or -1 otherwise.
*/
int make_write_non_block();
/**
* Get the read file descriptor (if any). The fetching of the file
* descriptor is not protected by a mutex, so if different threads in
* the same process are to manipulate the same pipe object with
* open(), close(), connect_to_stdin(), connect_to_stdout() or
* connect_to_stderr() (which manipulate both descriptors), or
* make_writeonly() (which manipulates the read file descriptor), or
* read the value of the file descriptor with this method or read()
* concurrently with such a manipulation, then the user should provide
* appropriate synchronisation. No exceptions will be thrown.
* @return The read file descriptor, or -1 is none is open.
*/
int get_read_fd() const {return read_fd;}
/**
* Get the write file descriptor (if any). The fetching of the file
* descriptor is not protected by a mutex, so if different threads in
* the same process are to manipulate the same pipe object with
* open(), close(), connect_to_stdin(), connect_to_stdout() or
* connect_to_stderr() (which manipulate both descriptors), or
* make_readonly() or make_write_non_block() (which manipulate the
* write file descriptor), or read the value of the file descriptor
* with this method or write() concurrently with such a manipulation,
* then the user should provide appropriate synchronisation. No
* exceptions will be thrown.
* @return The write file descriptor, or -1 is none is open.
*/
int get_write_fd() const {return write_fd;}
/**
* Connect the read file descriptor of the pipe to stdin. Once
* called, any read from stdin in the process which calls this method
* will be read from the pipe. It should only be called in a child
* process or parent process after a fork() where the PipeFifo object
* has been created in the parent process, and typically is called by
* the child process before one of the exec() functions is invoked by
* the child process. It should only be called once, and cannot be
* called after connect_to_stdout(), connect_to_stderr() or
* make_writeonly() has been called in the same process. Once called,
* the write file descriptor is no longer available to the process
* calling this method. Typically it would be used in order that when
* the new process image created by exec() reads from stdin, it reads
* from the pipe, and the parent process (or another child process)
* writes to the pipe. This function call is thread safe to the
* extent that all the system calls made in its implementation are
* async-signal-safe, so it may safely be called in a multi-threaded
* program after a fork() and before an exec(). However, the
* manipulation of the file descriptors and related variables is not
* protected by a mutex, so if different threads in the same process
* are to manipulate the same pipe object with this method, open(),
* close(), connect_to_stdout() or connect_to_stderr() (which
* manipulate both descriptors), make_readonly() or
* make_write_non_block() (which manipulate the write file
* descriptor), or make_writeonly() (which manipulates the read file
* descriptor), or read the value of the file descriptor with
* get_read_fd() or read() (which access the read file descriptor) or
* get_write_fd() or write() (which access the write file descriptor)
* concurrently with such a manipulation, then the user should provide
* appropriate synchronisation. It does not throw.
* @return -2 if the read file descriptor has previously been closed
* or this method, connect_to_stdout() or connect_to_stderr() has
* previously been called in the same process, otherwise it returns
* the value returned by dup2(), that is 0 if the operation succeeds,
* or -1 (with errno set) if not. It does not throw.
*/
int connect_to_stdin();
/**
* Connect the write file descriptor of the pipe to stdout. Once
* called, any write to stdout in the process which calls this method
* will be written to the pipe. It should only be called in a child
* or parent process after a fork() where the PipeFifo object has been
* created in the parent process, and typically is called by the child
* process before one of the exec() functions is invoked by the child
* process. It should only be called once, and cannot be called after
* connect_to_stdin(), connect_to_stderr() or make_readonly() has been
* called in the same process. Once called, the read file descriptor
* is no longer available to the process calling this method.
* Typically it would be used in order that when the new process image
* created by exec() writes to stdout, it writes to the pipe, and the
* parent process (or another child process) reads from the pipe.
* This function call is thread safe to the extent that all the system
* calls made in its implementation are async-signal-safe, so it may
* safely be called in a multi-threaded program after a fork() and
* before an exec(). However, the manipulation of the file
* descriptors and related variables is not protected by a mutex, so
* if different threads in the same process are to manipulate the same
* pipe object with this method, open(), close(), connect_to_stdin()
* or connect_to_stderr() (which manipulate both descriptors),
* make_readonly() or make_write_non_block() (which manipulate the
* write file descriptor), or make_writeonly() (which manipulates the
* read file descriptor), or read the value of the file descriptor
* with get_read_fd() or read() (which access the read file
* descriptor) or get_write_fd() or write() (which access the write
* file descriptor) concurrently with such a manipulation, then the
* user should provide appropriate synchronisation. It does not
* throw.
* @return -2 if the write file descriptor has previously been closed
* or this method, connect_to_stdin() or connect_to_stderr() has
* previously been called in the same process, otherwise it returns
* the value returned by dup2(), that is 0 if the operation succeeds,
* or -1 (with errno set) if not. It does not throw.
*/
int connect_to_stdout();
/**
* Connect the write file descriptor of the pipe to stderr. Once
* called, any write to stderr in the process which calls this method
* will be written to the pipe. It should only be called in a child
* or parent process after a fork() where the PipeFifo object has been
* created in the parent process, and typically is called by a child
* process before one of the exec() functions is invoked by the child
* process. It should only be called once, and cannot be called after
* connect_to_stdin(), connect_to_stdout() or make_readonly() has been
* called in the same process. Once called, the read file descriptor
* is no longer available to the process calling this method.
* Typically it would be used in order that when the new process image
* created by exec() writes to stderr, it writes to the pipe, and the
* parent process (or another child process) reads from the pipe.
* This function call is thread safe to the extent that all the system
* calls made in its implementation are async-signal-safe, so it may
* safely be called in a multi-threaded program after a fork() and
* before an exec(). However, the manipulation of the file
* descriptors and related variables is not protected by a mutex, so
* if different threads in the same process are to manipulate the same
* pipe object with this method, open(), close(), connect_to_stdin()
* or connect_to_stdout() (which manipulate both descriptors),
* make_readonly() or make_write_non_block() (which manipulate the
* write file descriptor), or make_writeonly() (which manipulates the
* read file descriptor), or read the value of the file descriptor
* with get_read_fd() or read() (which access the read file
* descriptor) or get_write_fd() or write() (which access the write
* file descriptor) concurrently with such a manipulation, then the
* user should provide appropriate synchronisation. It does not
* throw.
* @return -2 if the write file descriptor has previously been closed
* or this method, connect_to_stdin() or connect_to_stdout() has
* previously been called in the same process, otherwise it returns
* the value returned by dup2(), that is 0 if the operation succeeds,
* or -1 (with errno set) if not. It does not throw.
*/
int connect_to_stderr();
/**
* This constructor opens read and write file descriptors for the
* pipe. All the system calls made in its implementation are
* async-signal-safe.
* @param mode The mode in which the read file descriptor of the pipe
* is to be opened - either Cgu::PipeFifo::block or
* Cgu::PipeFifo::non_block.
* @exception Cgu::PipeError This exception is thrown if the opening
* of the pipe fails. No other exceptions will be thrown.
*/
PipeFifo(Fifo_mode mode);
/**
* The default constructor does not open any descriptors. open() must
* be called before the PipeFifo object can be used. It is
* async-signal-safe. It does not throw.
*/
PipeFifo();
/**
* The destructor does not throw. It is async-signal-safe.
*/
~PipeFifo() {close();}
/* Only has effect if --with-glib-memory-slices-compat or
* --with-glib-memory-slices-no-compat option picked */
CGU_GLIB_MEMORY_SLICES_FUNCS
};
/**
* @class SyncPipe pipes.h c++-gtk-utils/pipes.h
* @brief A class which uses an anonymous pipe to synchronise between
* processes.
* @sa PipeFifo
*
* This class enables synchronisation between processes after
* fork()ing. The process to wait on the other one calls wait() at
* the point where it wishes to wait, and the other process calls
* release() when it wants to enable the other process to continue.
* It is one-shot only - once it has released, it cannot re-block
* again. It is typically for use when a child process is setting
* itself up, or has a specific task to achieve, and needs to
* co-ordinate with the parent process or tell the parent process when
* it has done this. In such a case, the parent would wait until the
* child releases it.
*/
class SyncPipe {
PipeFifo pipe_fifo;
public:
/**
* Releases another process waiting on this pipe. If the other
* process has not yet called wait(), when it does so wait() will
* immediately return. This may safely be called after a
* multi-threaded process forks() (all the system calls made in its
* implementation are async-signal-safe), but only one thread should
* call it. It does not throw.
*/
void release() {pipe_fifo.make_writeonly(); pipe_fifo.make_readonly();}
/**
* Blocks until another process has called release() on this pipe. If
* the other process has already called release(), this function will
* immediately return. This may safely be called after a
* multi-threaded process forks() (all the system calls made in its
* implementation are async-signal-safe), but only one thread should
* call it. It does not throw.
*/
void wait();
/**
* All the system calls made in the constructor are async-signal-safe.
* @exception Cgu::PipeError This exception is thrown if the opening
* of the pipe fails. No other exceptions will be thrown.
*/
// using uniform initializer syntax here confuses doxygen
SyncPipe(): pipe_fifo(PipeFifo::block) {}
/**
* The destructor does not throw. It is async-signal-safe.
*/
~SyncPipe() {}
/* Only has effect if --with-glib-memory-slices-compat or
* --with-glib-memory-slices-no-compat option picked */
CGU_GLIB_MEMORY_SLICES_FUNCS
};
} // namespace Cgu
#endif