diff options
Diffstat (limited to 'libbutl/fdstream.hxx')
-rw-r--r-- | libbutl/fdstream.hxx | 917 |
1 files changed, 917 insertions, 0 deletions
diff --git a/libbutl/fdstream.hxx b/libbutl/fdstream.hxx new file mode 100644 index 0000000..4dc89a3 --- /dev/null +++ b/libbutl/fdstream.hxx @@ -0,0 +1,917 @@ +// file : libbutl/fdstream.hxx -*- C++ -*- +// license : MIT; see accompanying LICENSE file + +#pragma once + +#include <ios> // streamsize +#include <vector> +#include <string> +#include <chrono> +#include <istream> +#include <ostream> +#include <utility> // move(), pair +#include <cstdint> // uint16_t, uint64_t +#include <cstddef> // size_t + +#include <libbutl/path.hxx> +#include <libbutl/filesystem.hxx> // permissions, entry_stat +#include <libbutl/small-vector.hxx> +#include <libbutl/bufstreambuf.hxx> + +#include <libbutl/export.hxx> + +namespace butl +{ + // RAII type for file descriptors. Note that failure to close the descriptor + // is silently ignored by both the destructor and reset(). + // + // The descriptor can be negative. Such a descriptor is treated as unopened + // and is not closed. + // + struct nullfd_t + { + constexpr explicit nullfd_t (int) {} + constexpr operator int () const {return -1;} + }; + + constexpr nullfd_t nullfd (-1); + + class LIBBUTL_SYMEXPORT auto_fd + { + public: + auto_fd (nullfd_t = nullfd) noexcept: fd_ (-1) {} + + explicit + auto_fd (int fd) noexcept: fd_ (fd) {} + + auto_fd (auto_fd&& fd) noexcept: fd_ (fd.release ()) {} + auto_fd& operator= (auto_fd&&) noexcept; + + auto_fd (const auto_fd&) = delete; + auto_fd& operator= (const auto_fd&) = delete; + + ~auto_fd () noexcept; + + int + get () const noexcept {return fd_;} + + void + reset (int fd = -1) noexcept; + + int + release () noexcept + { + int r (fd_); + fd_ = -1; + return r; + } + + // Close an open file descriptor. Throw ios::failure on the underlying OS + // error. Reset the descriptor to -1 whether the exception is thrown or + // not. + // + void + close (); + + private: + int fd_; + }; + + inline bool + operator== (const auto_fd& x, const auto_fd& y) + { + return x.get () == y.get (); + } + + inline bool + operator!= (const auto_fd& x, const auto_fd& y) + { + return !(x == y); + } + + inline bool + operator== (const auto_fd& x, nullfd_t) + { + return x.get () == -1; + } + + inline bool + operator!= (const auto_fd& x, nullfd_t y) + { + return !(x == y); + } + + // An [io]fstream that can be initialized with a file descriptor in addition + // to a file name and that also by default enables exceptions on badbit and + // failbit. So instead of a dance like this: + // + // ifstream ifs; + // ifs.exceptions (ifstream::badbit | ifstream::failbit); + // ifs.open (path.string ()); + // + // You can simply do: + // + // ifdstream ifs (path); + // + // Notes and limitations: + // + // - char only + // - input or output but not both (can use a union of two streams for that) + // - no support for put back + // - use of tell[gp]() and seek[gp]() is discouraged on Windows for + // fdstreams opened in the text mode (see fdstreambuf::seekoff() + // implementation for reasoning and consider using non-standard tellg() + // and seekg() in fdstreambuf, instead) + // - non-blocking file descriptor is supported only by showmanyc() function + // and only for pipes on Windows, in contrast to POSIX systems + // - throws ios::failure in case of open(), read(), write(), close(), + // seek[gp](), or tell[gp]() errors + // - exception mask has at least badbit + // - after catching an exception caused by badbit the stream is no longer + // usable + // - not movable, though can be easily supported (or not: there is no move + // constructor for istream/ostream in GCC 4.9) + // - passing to constructor auto_fd with a negative file descriptor is valid + // and results in the creation of an unopened object + // + class LIBBUTL_SYMEXPORT fdstreambuf: public bufstreambuf + { + public: + fdstreambuf () = default; + + // Unless specified, the current read/write position is assumed to + // be 0 (note: not queried). + // + fdstreambuf (auto_fd&&, std::uint64_t pos = 0); + + // Before we invented auto_fd into fdstreams we keept fdstreambuf opened + // on faulty close attempt. Now fdstreambuf is always closed by close() + // function. This semantics change seems to be the right one as there is + // no reason to expect fdclose() to succeed after it has already failed + // once. + // + void + close () {fd_.close ();} + + auto_fd + release (); + + void + open (auto_fd&&, std::uint64_t pos = 0); + + bool + is_open () const {return fd_.get () >= 0;} + + int + fd () const {return fd_.get ();} + + // Set the file descriptor blocking mode returning the previous mode on + // success and throwing ios::failure otherwise (see fdmode() for details). + // + // Note that besides calling fdmode(fd()), this function also updating its + // internal state according to the new mode. + // + bool + blocking (bool); + + public: + using base = bufstreambuf; + + // basic_streambuf input interface. + // + public: + virtual std::streamsize + showmanyc (); + + virtual int_type + underflow (); + + // Direct access to the get area. Use with caution. + // + using base::gptr; + using base::egptr; + using base::gbump; + + // Return the (logical) position of the next byte to be read. + // + using base::tellg; + + // Seek to the (logical) position as if by reading the specified number of + // bytes from the beginning of the stream. Throw ios::failure on the + // underlying OS errors. + // + void + seekg (std::uint64_t); + + private: + bool + load (); + + // basic_streambuf output interface. + // + public: + virtual int_type + overflow (int_type); + + virtual int + sync (); + + virtual std::streamsize + xsputn (const char_type*, std::streamsize); + + // Return the (logical) position of the next byte to be written. + // + using base::tellp; + + // basic_streambuf positioning interface (both input/output). + // + public: + virtual pos_type + seekpos (pos_type, std::ios_base::openmode); + + virtual pos_type + seekoff (off_type, std::ios_base::seekdir, std::ios_base::openmode); + + private: + bool + save (); + + private: + auto_fd fd_; + char buf_[8192]; + bool non_blocking_ = false; + }; + + // File stream mode. + // + // The text/binary flags have the same semantics as those in std::fstream. + // Specifically, this is a noop for POSIX systems where the two modes are + // the same. On Windows, when reading in the text mode the sequence of 0xD, + // 0xA characters is translated into the single OxA character and 0x1A is + // interpreted as EOF. When writing in the text mode the OxA character is + // translated into the 0xD, 0xA sequence. + // + // The skip flag instructs the stream to skip to the end before closing the + // file descriptor. This is primarily useful when working with pipes where + // you may want not to "offend" the other end by closing your end before + // reading all the data. + // + // The blocking/non_blocking flags determine whether the IO operation should + // block or return control if currently there is no data to read or no room + // to write. Only the istream::readsome() function supports the semantics of + // non-blocking operations. In contrast to POSIX systems, we only support + // this for pipes on Windows, always assuming the blocking mode for other + // file descriptors. IO stream operations other than readsome() are illegal + // in the non-blocking mode and result in the badbit being set (note that + // it is not the more appropriate failbit for implementation reasons). + // + enum class fdstream_mode: std::uint16_t + { + text = 0x01, + binary = 0x02, + skip = 0x04, + blocking = 0x08, + non_blocking = 0x10 + }; + + inline fdstream_mode operator& (fdstream_mode, fdstream_mode); + inline fdstream_mode operator| (fdstream_mode, fdstream_mode); + inline fdstream_mode operator&= (fdstream_mode&, fdstream_mode); + inline fdstream_mode operator|= (fdstream_mode&, fdstream_mode); + + // Extended (compared to ios::openmode) file open flags. + // + enum class fdopen_mode: std::uint16_t + { + in = 0x01, // Open for reading. + out = 0x02, // Open for writing. + append = 0x04, // Seek to the end of file before each write. + truncate = 0x08, // Discard the file contents on open. + create = 0x10, // Create a file if not exists. + exclusive = 0x20, // Fail if the file exists and the create flag is set. + binary = 0x40, // Set binary translation mode. + at_end = 0x80, // Seek to the end of stream immediately after open. + + none = 0 // Usefull when building the mode incrementally. + }; + + inline fdopen_mode operator& (fdopen_mode, fdopen_mode); + inline fdopen_mode operator| (fdopen_mode, fdopen_mode); + inline fdopen_mode operator&= (fdopen_mode&, fdopen_mode); + inline fdopen_mode operator|= (fdopen_mode&, fdopen_mode); + + class LIBBUTL_SYMEXPORT fdstream_base + { + protected: + fdstream_base () = default; + fdstream_base (auto_fd&&, std::uint64_t pos); + fdstream_base (auto_fd&&, fdstream_mode, std::uint64_t pos); + + public: + int + fd () const {return buf_.fd ();} + + protected: + fdstreambuf buf_; + }; + + // iofdstream constructors and open() functions that take openmode as an + // argument mimic the corresponding iofstream functions in terms of the + // openmode mask interpretation. They throw std::invalid_argument for an + // invalid combination of flags (as per the standard). Note that the in and + // out flags are always added implicitly for ifdstream and ofdstream, + // respectively. + // + // iofdstream constructors and open() functions that take fdopen_mode as an + // argument interpret the mask literally just ignoring some flags which are + // meaningless in the absense of others (read more on that in the comment + // for fdopen()). Note that the in and out flags are always added implicitly + // for ifdstream and ofdstream, respectively. + // + // iofdstream constructors and open() functions that take file path as a + // const std::string& or const char* may throw the invalid_path exception. + // + // Passing auto_fd with a negative file descriptor is valid and results in + // the creation of an unopened object. + // + // Also note that open() and close() functions can be successfully called + // for an opened and unopened objects respectively. That is in contrast with + // iofstream that sets failbit in such cases. + // + + // Note that ifdstream destructor will close an open file descriptor but + // will ignore any errors. To detect such errors, call close() explicitly. + // + // This is a sample usage of iofdstreams with process. Note that here it is + // expected that the child process reads from STDIN first and writes to + // STDOUT afterwards. + // + // try + // { + // process pr (args, -1, -1); + // + // try + // { + // // In case of exception, skip and close input after output. + // // + // ifdstream is (move (pr.in_ofd), fdstream_mode::skip); + // ofdstream os (move (pr.out_fd)); + // + // // Write. + // + // os.close (); // Don't block the other end. + // + // // Read. + // + // is.close (); // Skip till end and close. + // + // if (pr.wait ()) + // { + // return ...; // Good. + // } + // + // // Non-zero exit, diagnostics presumably issued, fall through. + // } + // catch (const failure&) + // { + // // IO failure, child exit status doesn't matter. Just wait for the + // // process completion and fall through. + // // + // // Note that this is optional if the process_error handler simply + // // falls through since process destructor will wait (but will ignore + // // any errors). + // // + // pr.wait (); + // } + // + // error << .... ; + // + // // Fall through. + // } + // catch (const process_error& e) + // { + // error << ... << e; + // + // if (e.child ()) + // exit (1); + // + // // Fall through. + // } + // + // throw failed (); + // + class LIBBUTL_SYMEXPORT ifdstream: public fdstream_base, public std::istream + { + public: + // Create an unopened object. + // + explicit + ifdstream (iostate = badbit | failbit); + + explicit + ifdstream (auto_fd&&, + iostate = badbit | failbit, + std::uint64_t pos = 0); + + ifdstream (auto_fd&&, + fdstream_mode m, + iostate = badbit | failbit, + std::uint64_t pos = 0); + + explicit + ifdstream (const char*, + iostate = badbit | failbit); + + explicit + ifdstream (const std::string&, + iostate = badbit | failbit); + + explicit + ifdstream (const path&, + iostate = badbit | failbit); + + // @@ In some implementations (for example, MSVC), iostate and openmode + // (and/or their respective constants) are not distinct enough which + // causes overload resolution errors. + // + ifdstream (const char*, + openmode, + iostate /*= badbit | failbit*/); + + ifdstream (const std::string&, + openmode, + iostate /*= badbit | failbit*/); + + ifdstream (const path&, + openmode, + iostate /*= badbit | failbit*/); + + ifdstream (const char*, + fdopen_mode, + iostate = badbit | failbit); + + ifdstream (const std::string&, + fdopen_mode, + iostate = badbit | failbit); + + ifdstream (const path&, + fdopen_mode, + iostate = badbit | failbit); + + ~ifdstream () override; + + void + open (const char*, openmode = in); + + void + open (const std::string&, openmode = in); + + void + open (const path&, openmode = in); + + void + open (const char*, fdopen_mode); + + void + open (const std::string&, fdopen_mode); + + void + open (const path&, fdopen_mode); + + void + open (auto_fd&& fd, std::uint64_t pos = 0) + { + buf_.open (std::move (fd), pos); + clear (); + } + + void + open (auto_fd&& fd, fdstream_mode m, std::uint64_t pos = 0); + + void close (); + auto_fd release (); // Note: no skipping. + bool is_open () const {return buf_.is_open ();} + + // Read the textual stream. The stream is supposed not to contain the null + // character. + // + std::string + read_text (); + + // Read the binary stream. + // + std::vector<char> + read_binary (); + + private: + bool skip_ = false; + }; + + // Note that ofdstream requires that you explicitly call close() before + // destroying it. Or, more specifically, the ofdstream object should not be + // in the opened state by the time its destructor is called, unless it is in + // the "not good" state (good() == false) or the destructor is being called + // during the stack unwinding due to an exception being thrown + // (std::uncaught_exception() == true). This is enforced with assert() in + // the ofdstream destructor. + // + class LIBBUTL_SYMEXPORT ofdstream: public fdstream_base, public std::ostream + { + public: + // Create an unopened object. + // + explicit + ofdstream (iostate = badbit | failbit); + + explicit + ofdstream (auto_fd&&, + iostate = badbit | failbit, + std::uint64_t pos = 0); + + ofdstream (auto_fd&&, + fdstream_mode m, + iostate = badbit | failbit, + std::uint64_t pos = 0); + + explicit + ofdstream (const char*, + iostate = badbit | failbit); + + explicit + ofdstream (const std::string&, + iostate = badbit | failbit); + + explicit + ofdstream (const path&, + iostate = badbit | failbit); + + // @@ In some implementations (for example, MSVC), iostate and openmode + // (and/or their respective constants) are not distinct enough which + // causes overload resolution errors. + // + ofdstream (const char*, + openmode, + iostate /*= badbit | failbit*/); + + ofdstream (const std::string&, + openmode, + iostate /*= badbit | failbit*/); + + ofdstream (const path&, + openmode, + iostate /*= badbit | failbit*/); + + ofdstream (const char*, + fdopen_mode, + iostate = badbit | failbit); + + ofdstream (const std::string&, + fdopen_mode, + iostate = badbit | failbit); + + ofdstream (const path&, + fdopen_mode, + iostate = badbit | failbit); + + ~ofdstream () override; + + void + open (const char*, openmode = out); + + void + open (const std::string&, openmode = out); + + void + open (const path&, openmode = out); + + void + open (const char*, fdopen_mode); + + void + open (const std::string&, fdopen_mode); + + void + open (const path&, fdopen_mode); + + void + open (auto_fd&& fd, std::uint64_t pos = 0) + { + buf_.open (std::move (fd), pos); + clear (); + } + + void close () {if (is_open ()) flush (); buf_.close ();} + auto_fd release (); + bool is_open () const {return buf_.is_open ();} + }; + + // Open a file or, if the file name is `-`, stdin/stdout. + // + // In case of the stdin/stdout, these functions simply adjust the exception + // mask on std::cin/cout to match the i/ofdstreams argument. + // + // Return a reference to the opened i/ofdstream or cin/cout and, in the + // latter case, set the translated name in path_name to <stdin>/<stdout> + // (unless it is already present). + // + // Note that ofdstream::close() should be called explicitly unless stdout + // was opened (but harmless to call even if it was). + // + LIBBUTL_SYMEXPORT std::istream& + open_file_or_stdin (path_name&, ifdstream&); + + LIBBUTL_SYMEXPORT std::ostream& + open_file_or_stdout (path_name&, ofdstream&); + + // The std::getline() replacement that provides a workaround for libstdc++'s + // ios::failure ABI fiasco (#66145) by throwing ios::failure, as it is + // defined at libbutl build time (new ABI on recent distributions) rather + // than libstdc++ build time (still old ABI on most distributions). + // + // Notes: + // + // - This relies of ADL so if the stream is used via the std::istream + // interface, then std::getline() will still be used. To put it another + // way, this is "the best we can do" until GCC folks get their act + // together. + // + // - The fail and eof bits may be left cleared in the stream exception mask + // when the function throws because of badbit. + // + LIBBUTL_SYMEXPORT ifdstream& + getline (ifdstream&, std::string&, char delim = '\n'); + + // Open a file returning an auto_fd that holds its file descriptor on + // success and throwing ios::failure otherwise. + // + // The mode argument should have at least one of the in or out flags set. + // The append and truncate flags are meaningless in the absense of the out + // flag and are ignored without it. The exclusive flag is meaningless in the + // absense of the create flag and is ignored without it. Note also that if + // the exclusive flag is specified then a dangling symbolic link is treated + // as an existing file. + // + // The permissions argument is taken into account only if the file is + // created. Note also that permissions can be adjusted while being set in a + // way specific for the OS. On POSIX systems they are modified with the + // process' umask, so effective permissions are permissions & ~umask. On + // Windows permissions other than ru and wu are unlikelly to have effect. + // + // Also note that on POSIX the FD_CLOEXEC flag is set for the file descriptor + // to prevent its leakage into child processes. On Windows, for the same + // purpose, the _O_NOINHERIT flag is set. Note that the process class, that + // passes such a descriptor to the child, makes it inheritable for a while. + // + LIBBUTL_SYMEXPORT auto_fd + fdopen (const char*, + fdopen_mode, + permissions = permissions::ru | permissions::wu | + permissions::rg | permissions::wg | + permissions::ro | permissions::wo); + + LIBBUTL_SYMEXPORT auto_fd + fdopen (const std::string&, + fdopen_mode, + permissions = permissions::ru | permissions::wu | + permissions::rg | permissions::wg | + permissions::ro | permissions::wo); + + LIBBUTL_SYMEXPORT auto_fd + fdopen (const path&, + fdopen_mode, + permissions = permissions::ru | permissions::wu | + permissions::rg | permissions::wg | + permissions::ro | permissions::wo); + + // Duplicate an open file descriptor. Throw ios::failure on the underlying + // OS error. + // + // Note that on POSIX the FD_CLOEXEC flag is set for the new descriptor if it + // is present for the source one. That's in contrast to POSIX dup() that + // doesn't copy file descriptor flags. Also note that duplicating descriptor + // and setting the flag is not an atomic operation generally, but it is in + // regards to child process spawning (to prevent file descriptor leakage into + // a child process). + // + // Note that on Windows the _O_NOINHERIT flag is set for the new descriptor + // if it is present for the source one. That's in contrast to Windows _dup() + // that doesn't copy the flag. Also note that duplicating descriptor and + // setting the flag is not an atomic operation generally, but it is in + // regards to child process spawning (to prevent file descriptor leakage into + // a child process). + // + LIBBUTL_SYMEXPORT auto_fd + fddup (int fd); + + // Set the translation and/or blocking modes for the file descriptor. Throw + // invalid_argument for an invalid combination of flags. Return the previous + // mode on success, throw ios::failure otherwise. + // + // The text and binary flags are mutually exclusive on Windows. On POSIX + // system the two modes are the same and so no check is performed. + // + // The blocking and non-blocking flags are mutually exclusive. In contrast + // to POSIX systems, on Windows the non-blocking mode is only supported for + // pipes, with the blocking mode assumed for other file descriptors + // regardless of the flags. + // + // Note that on Wine currently pipes always behave as blocking regardless of + // the mode set. + // + LIBBUTL_SYMEXPORT fdstream_mode + fdmode (int, fdstream_mode); + + // Portable functions for obtaining file descriptors of standard streams. + // Throw ios::failure on the underlying OS error. + // + // Note that you normally wouldn't want to close them using fddup() to + // convert them to auto_fd, for example: + // + // ifdstream is (fddup (stdin_fd ())); + // + LIBBUTL_SYMEXPORT int stdin_fd (); + LIBBUTL_SYMEXPORT int stdout_fd (); + LIBBUTL_SYMEXPORT int stderr_fd (); + + // Convenience functions for setting the translation mode for standard + // streams. + // + LIBBUTL_SYMEXPORT fdstream_mode stdin_fdmode (fdstream_mode); + LIBBUTL_SYMEXPORT fdstream_mode stdout_fdmode (fdstream_mode); + LIBBUTL_SYMEXPORT fdstream_mode stderr_fdmode (fdstream_mode); + + // Low-level, nothrow file descriptor API. + // + + // Close the file descriptor. Return true on success, set errno and return + // false otherwise. + // + LIBBUTL_SYMEXPORT bool + fdclose (int) noexcept; + + // Open the null device (e.g., /dev/null) that discards all data written to + // it and provides no data for read operations (i.e., yelds EOF on read). + // Return an auto_fd that holds its file descriptor on success and throwing + // ios::failure otherwise. + // + // On Windows the null device is NUL and writing anything substantial to it + // (like redirecting a process' output) is extremely slow, as in, an order + // of magnitude slower than writing to disk. If you are using the descriptor + // yourself this can be mitigated by setting the binary mode (already done + // by fdopen()) and using a buffer of around 64K. However, sometimes you + // have no control of how the descriptor will be used. For instance, it can + // be used to redirect a child's stdout and the way the child sets up its + // stdout is out of your control (on Windows). For such cases, there is an + // emulation via a temporary file. Mostly it functions as a proper null + // device with the file automatically removed once the descriptor is + // closed. One difference, however, would be if you were to both write to + // and read from the descriptor. + // + // Note that on POSIX the FD_CLOEXEC flag is set for the file descriptor to + // prevent its leakage into child processes. On Windows, for the same + // purpose, the _O_NOINHERIT flag is set. + // +#ifndef _WIN32 + LIBBUTL_SYMEXPORT auto_fd + fdopen_null (); +#else + LIBBUTL_SYMEXPORT auto_fd + fdopen_null (bool temp = false); +#endif + + struct fdpipe + { + auto_fd in; + auto_fd out; + + void + close () + { + in.close (); + out.close (); + } + }; + + // Create a pipe. Throw ios::failure on the underlying OS error. By default + // both ends of the pipe are opened in the text mode. Pass the binary flag + // to instead open them in the binary mode. Passing a mode other than none + // or binary is illegal. + // + // Note that on Windows both ends of the created pipe are not inheritable. + // In particular, the process class that uses fdpipe underneath makes the + // appropriate end (the one being passed to the child) inheritable. + // + // Note that on POSIX the FD_CLOEXEC flag is set for both ends, so they get + // automatically closed by the child process to prevent undesired behaviors + // (such as child deadlock on read from a pipe due to the write-end leakage + // into the child process). Opening a pipe and setting the flag is not an + // atomic operation generally, but it is in regards to child process spawning + // (to prevent file descriptor leakage into child processes spawned from + // other threads). Also note that you don't need to reset the flag for a pipe + // end being passed to the process class ctor. + // + LIBBUTL_SYMEXPORT fdpipe + fdopen_pipe (fdopen_mode = fdopen_mode::none); + + // Seeking. + // + enum class fdseek_mode {set, cur, end}; + + LIBBUTL_SYMEXPORT std::uint64_t + fdseek (int, std::int64_t, fdseek_mode); + + // Truncate or expand the file to the specified size. Throw ios::failure on + // the underlying OS error. + // + LIBBUTL_SYMEXPORT void + fdtruncate (int, std::uint64_t); + + // Return filesystem entry stat from file descriptor. Throw ios::failure on + // the underlying OS error. + // + // See also path_entry() in filesystem. + // + LIBBUTL_SYMEXPORT entry_stat + fdstat (int); + + // Test whether a file descriptor refers to a terminal. Throw ios::failure + // on the underlying OS error. + // + LIBBUTL_SYMEXPORT bool + fdterm (int); + + // Wait until one or more file descriptors becomes ready for input (reading) + // or output (writing). Return the pair of numbers of descriptors that are + // ready. Throw std::invalid_argument if anything is wrong with arguments + // (both sets are empty, invalid fd, etc). Throw ios::failure on the + // underlying OS error. + // + // Note that the function clears all the previously-ready entries on each + // call. Entries with nullfd are ignored. + // + // On Windows only pipes and only their input (read) ends are supported. + // + struct fdselect_state + { + int fd; + bool ready; + + // Note: intentionally non-explicit to allow implicit initialization when + // pushing to fdselect_set. + // + fdselect_state (int fd): fd (fd), ready (false) {} + }; + + using fdselect_set = small_vector<fdselect_state, 4>; + + LIBBUTL_SYMEXPORT std::pair<std::size_t, std::size_t> + fdselect (fdselect_set& ifds, fdselect_set& ofds); + + inline std::size_t + ifdselect (fdselect_set& ifds) + { + fdselect_set ofds; + return fdselect (ifds, ofds).first; + } + + inline std::size_t + ofdselect (fdselect_set& ofds) + { + fdselect_set ifds; + return fdselect (ifds, ofds).second; + } + + // As above but wait up to the specified timeout returning a pair of zeroes + // if none of the descriptors became ready. + // + template <typename R, typename P> + std::pair<std::size_t, std::size_t> + fdselect (fdselect_set&, fdselect_set&, const std::chrono::duration<R, P>&); + + template <typename R, typename P> + inline std::size_t + ifdselect (fdselect_set& ifds, const std::chrono::duration<R, P>& timeout) + { + fdselect_set ofds; + return fdselect (ifds, ofds, timeout).first; + } + + template <typename R, typename P> + inline std::size_t + ofdselect (fdselect_set& ofds, const std::chrono::duration<R, P>& timeout) + { + fdselect_set ifds; + return fdselect (ifds, ofds, timeout).second; + } + + // POSIX read() function wrapper. In particular, it supports the semantics + // of non-blocking read for pipes on Windows. + // + // Note that on Wine currently pipes always behave as blocking regardless of + // the mode. + // + LIBBUTL_SYMEXPORT std::streamsize + fdread (int, void*, std::size_t); +} + +#include <libbutl/fdstream.ixx> |