c++-gtk-utils
|
A wrapper for unix anonymous pipes. More...
#include <c++-gtk-utils/pipes.h>
Public Types | |
enum | Fifo_mode { block, non_block } |
Public Member Functions | |
void | open (Fifo_mode mode) |
void | close () |
ssize_t | read (char *buf, size_t max_num) |
int | read () |
ssize_t | write (const char *str) |
ssize_t | write (const char *buf, size_t num) |
int | write (char item) |
void | make_writeonly () |
void | make_readonly () |
int | make_write_non_block () |
int | get_read_fd () const |
int | get_write_fd () const |
int | connect_to_stdin () |
int | connect_to_stdout () |
int | connect_to_stderr () |
PipeFifo (Fifo_mode mode) | |
PipeFifo () | |
~PipeFifo () | |
A wrapper for unix anonymous pipes.
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.
Cgu::PipeFifo::PipeFifo | ( | Fifo_mode | mode | ) |
This constructor opens read and write file descriptors for the pipe. All the system calls made in its implementation are async-signal-safe.
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. |
Cgu::PipeError | This exception is thrown if the opening of the pipe fails. No other exceptions will be thrown. |
Cgu::PipeFifo::PipeFifo | ( | ) |
|
inline |
The destructor does not throw. It is async-signal-safe.
void Cgu::PipeFifo::close | ( | ) |
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.
int Cgu::PipeFifo::connect_to_stderr | ( | ) |
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.
int Cgu::PipeFifo::connect_to_stdin | ( | ) |
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.
int Cgu::PipeFifo::connect_to_stdout | ( | ) |
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.
|
inline |
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.
|
inline |
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.
void Cgu::PipeFifo::make_readonly | ( | ) |
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.
int Cgu::PipeFifo::make_write_non_block | ( | ) |
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.
void Cgu::PipeFifo::make_writeonly | ( | ) |
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 Cgu::PipeFifo::open | ( | Fifo_mode | mode | ) |
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.
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. |
Cgu::PipeError | This exception is thrown if the opening of the pipe fails. No other exceptions will be thrown. |
int Cgu::PipeFifo::read | ( | ) |
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.
ssize_t Cgu::PipeFifo::read | ( | char * | buf, |
size_t | max_num | ||
) |
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.
buf | A buffer into which the characters will be placed. |
max_num | The maximum number of characters to be read (normally the size of the buffer). |
|
inline |
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.
item | The character to be written. |
ssize_t Cgu::PipeFifo::write | ( | const char * | buf, |
size_t | num | ||
) |
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.
buf | The buffer to be written. |
num | The number of characters in the buffer. |
ssize_t Cgu::PipeFifo::write | ( | const char * | str | ) |
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.
str | The string to be written. |