diff options
Diffstat (limited to 'include/ruby/io.h')
| -rw-r--r-- | include/ruby/io.h | 411 |
1 files changed, 274 insertions, 137 deletions
diff --git a/include/ruby/io.h b/include/ruby/io.h index dc4c8becf6..ed0967abad 100644 --- a/include/ruby/io.h +++ b/include/ruby/io.h @@ -49,20 +49,43 @@ /** @endcond */ #include "ruby/internal/attr/const.h" +#include "ruby/internal/attr/packed_struct.h" #include "ruby/internal/attr/pure.h" #include "ruby/internal/attr/noreturn.h" #include "ruby/internal/dllexport.h" #include "ruby/internal/value.h" -#include "ruby/backward/2/attributes.h" /* PACKED_STRUCT_UNALIGNED */ // IO#wait, IO#wait_readable, IO#wait_writable, IO#wait_priority are defined by this implementation. #define RUBY_IO_WAIT_METHODS +// Used as the default timeout argument to `rb_io_wait` to use the `IO#timeout` value. +#define RUBY_IO_TIMEOUT_DEFAULT Qnil + RBIMPL_SYMBOL_EXPORT_BEGIN() struct stat; struct timeval; +#if defined(HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC) +# define RUBY_USE_STATX 0 +#elif defined(HAVE_STRUCT_STATX_STX_BTIME) +# define RUBY_USE_STATX 1 +struct statx; +#else +# define RUBY_USE_STATX 0 +#endif + +#if RUBY_USE_STATX +typedef struct statx rb_io_stat_data; +#else +typedef struct stat rb_io_stat_data; +#endif + +/** + * Indicates that a timeout has occurred while performing an IO operation. + */ +RUBY_EXTERN VALUE rb_eIOTimeoutError; + /** * Type of events that an IO can wait. * @@ -70,17 +93,20 @@ struct timeval; * * This is visible from extension libraries because `io/wait` wants it. */ -typedef enum { +enum rb_io_event { RUBY_IO_READABLE = RB_WAITFD_IN, /**< `IO::READABLE` */ RUBY_IO_WRITABLE = RB_WAITFD_OUT, /**< `IO::WRITABLE` */ RUBY_IO_PRIORITY = RB_WAITFD_PRI, /**< `IO::PRIORITY` */ -} rb_io_event_t; +}; + +typedef enum rb_io_event rb_io_event_t; /** * IO buffers. This is an implementation detail of ::rb_io_t::wbuf and * ::rb_io_t::rbuf. People don't manipulate it directly. */ -PACKED_STRUCT_UNALIGNED(struct rb_io_buffer_t { +RBIMPL_ATTR_PACKED_STRUCT_UNALIGNED_BEGIN() +struct rb_io_internal_buffer { /** Pointer to the underlying memory region, of at least `capa` bytes. */ char *ptr; /* off + len <= capa */ @@ -93,10 +119,10 @@ PACKED_STRUCT_UNALIGNED(struct rb_io_buffer_t { /** Designed capacity of the buffer. */ int capa; -}); +} RBIMPL_ATTR_PACKED_STRUCT_UNALIGNED_END(); /** @alias{rb_io_buffer_t} */ -typedef struct rb_io_buffer_t rb_io_buffer_t; +typedef struct rb_io_internal_buffer rb_io_buffer_t; /** Decomposed encoding flags (e.g. `"enc:enc2""`). */ /* @@ -105,7 +131,7 @@ typedef struct rb_io_buffer_t rb_io_buffer_t; * e1 NULL force_encoding(e1) convert str.encoding to e1 * e1 e2 convert from e2 to e1 convert str.encoding to e2 */ -struct rb_io_enc_t { +struct rb_io_encoding { /** Internal encoding. */ rb_encoding *enc; /** External encoding. */ @@ -126,108 +152,6 @@ struct rb_io_enc_t { VALUE ecopts; }; -/** Ruby's IO, metadata and buffers. */ -typedef struct rb_io_t { - - /** The IO's Ruby level counterpart. */ - VALUE self; - - /** stdio ptr for read/write, if available. */ - FILE *stdio_file; - - /** file descriptor. */ - int fd; - - /** mode flags: FMODE_XXXs */ - int mode; - - /** child's pid (for pipes) */ - rb_pid_t pid; - - /** number of lines read */ - int lineno; - - /** pathname for file */ - VALUE pathv; - - /** finalize proc */ - void (*finalize)(struct rb_io_t*,int); - - /** Write buffer. */ - rb_io_buffer_t wbuf; - - /** - * (Byte) read buffer. Note also that there is a field called - * ::rb_io_t::cbuf, which also concerns read IO. - */ - rb_io_buffer_t rbuf; - - /** - * Duplex IO object, if set. - * - * @see rb_io_set_write_io() - */ - VALUE tied_io_for_writing; - - struct rb_io_enc_t encs; /**< Decomposed encoding flags. */ - - /** Encoding converter used when reading from this IO. */ - rb_econv_t *readconv; - - /** - * rb_io_ungetc() destination. This buffer is read before checking - * ::rb_io_t::rbuf - */ - rb_io_buffer_t cbuf; - - /** Encoding converter used when writing to this IO. */ - rb_econv_t *writeconv; - - /** - * This is, when set, an instance of ::rb_cString which holds the "common" - * encoding. Write conversion can convert strings twice... In case - * conversion from encoding X to encoding Y does not exist, Ruby finds an - * encoding Z that bridges the two, so that X to Z to Y conversion happens. - */ - VALUE writeconv_asciicompat; - - /** Whether ::rb_io_t::writeconv is already set up. */ - int writeconv_initialized; - - /** - * Value of ::rb_io_t::rb_io_enc_t::ecflags stored right before - * initialising ::rb_io_t::writeconv. - */ - int writeconv_pre_ecflags; - - /** - * Value of ::rb_io_t::rb_io_enc_t::ecopts stored right before initialising - * ::rb_io_t::writeconv. - */ - VALUE writeconv_pre_ecopts; - - /** - * This is a Ruby level mutex. It avoids multiple threads to write to an - * IO at once; helps for instance rb_io_puts() to ensure newlines right - * next to its arguments. - * - * This of course doesn't help inter-process IO interleaves, though. - */ - VALUE write_lock; -} rb_io_t; - -/** @alias{rb_io_enc_t} */ -typedef struct rb_io_enc_t rb_io_enc_t; - -/** - * @private - * - * @deprecated This macro once was a thing in the old days, but makes no sense - * any longer today. Exists here for backwards compatibility - * only. You can safely forget about it. - */ -#define HAVE_RB_IO_T 1 - /** * @name Possible flags for ::rb_io_t::mode * @@ -317,7 +241,16 @@ typedef struct rb_io_enc_t rb_io_enc_t; * Setting this one and #FMODE_BINMODE at the same time is a contradiction. */ #define FMODE_TEXTMODE 0x00001000 -/* #define FMODE_PREP 0x00010000 */ +/** + * This flag means that an IO object is wrapping an "external" file descriptor, + * which is owned by something outside the Ruby interpreter (usually a C extension). + * Ruby will not close this file when the IO object is garbage collected. + * If this flag is set, then IO#autoclose? is false, and vice-versa. + * + * This flag was previously called FMODE_PREP internally. + */ +#define FMODE_EXTERNAL 0x00010000 + /* #define FMODE_SIGNAL_ON_EPIPE 0x00020000 */ /** @@ -331,6 +264,166 @@ typedef struct rb_io_enc_t rb_io_enc_t; /** @} */ +enum rb_io_mode { + RUBY_IO_MODE_EXTERNAL = FMODE_EXTERNAL, + + RUBY_IO_MODE_READABLE = FMODE_READABLE, + RUBY_IO_MODE_WRITABLE = FMODE_WRITABLE, + RUBY_IO_MODE_READABLE_WRITABLE = (RUBY_IO_MODE_READABLE|RUBY_IO_MODE_WRITABLE), + + RUBY_IO_MODE_BINARY = FMODE_BINMODE, + RUBY_IO_MODE_TEXT = FMODE_TEXTMODE, + RUBY_IO_MODE_TEXT_SET_ENCODING_FROM_BOM = FMODE_SETENC_BY_BOM, + + RUBY_IO_MODE_SYNCHRONISED = FMODE_SYNC, + + RUBY_IO_MODE_TTY = FMODE_TTY, + + RUBY_IO_MODE_DUPLEX = FMODE_DUPLEX, + + RUBY_IO_MODE_APPEND = FMODE_APPEND, + RUBY_IO_MODE_CREATE = FMODE_CREATE, + RUBY_IO_MODE_EXCLUSIVE = FMODE_EXCL, + RUBY_IO_MODE_TRUNCATE = FMODE_TRUNC, +}; + +typedef enum rb_io_mode rb_io_mode_t; + +#ifndef HAVE_RB_IO_T +#define HAVE_RB_IO_T 1 +/** Ruby's IO, metadata and buffers. */ +struct rb_io { + /** The IO's Ruby level counterpart. */ + RBIMPL_ATTR_DEPRECATED(("with no replacement")) + VALUE self; + + /** stdio ptr for read/write, if available. */ + RBIMPL_ATTR_DEPRECATED(("with no replacement")) + FILE *stdio_file; + + /** file descriptor. */ + RBIMPL_ATTR_DEPRECATED(("rb_io_descriptor")) + int fd; + + /** mode flags: FMODE_XXXs */ + RBIMPL_ATTR_DEPRECATED(("rb_io_mode")) + enum rb_io_mode mode; + + /** child's pid (for pipes) */ + RBIMPL_ATTR_DEPRECATED(("with no replacement")) + rb_pid_t pid; + + /** number of lines read */ + RBIMPL_ATTR_DEPRECATED(("with no replacement")) + int lineno; + + /** pathname for file */ + RBIMPL_ATTR_DEPRECATED(("rb_io_path")) + VALUE pathv; + + /** finalize proc */ + RBIMPL_ATTR_DEPRECATED(("with no replacement")) + void (*finalize)(struct rb_io*,int); + + /** Write buffer. */ + RBIMPL_ATTR_DEPRECATED(("with no replacement")) + rb_io_buffer_t wbuf; + + /** + * (Byte) read buffer. Note also that there is a field called + * ::rb_io_t::cbuf, which also concerns read IO. + */ + RBIMPL_ATTR_DEPRECATED(("with no replacement")) + rb_io_buffer_t rbuf; + + /** + * Duplex IO object, if set. + * + * @see rb_io_set_write_io() + */ + RBIMPL_ATTR_DEPRECATED(("rb_io_get_write_io")) + VALUE tied_io_for_writing; + + RBIMPL_ATTR_DEPRECATED(("with no replacement")) + struct rb_io_encoding encs; /**< Decomposed encoding flags. */ + + /** Encoding converter used when reading from this IO. */ + RBIMPL_ATTR_DEPRECATED(("with no replacement")) + rb_econv_t *readconv; + + /** + * rb_io_ungetc() destination. This buffer is read before checking + * ::rb_io_t::rbuf + */ + RBIMPL_ATTR_DEPRECATED(("with no replacement")) + rb_io_buffer_t cbuf; + + /** Encoding converter used when writing to this IO. */ + RBIMPL_ATTR_DEPRECATED(("with no replacement")) + rb_econv_t *writeconv; + + /** + * This is, when set, an instance of ::rb_cString which holds the "common" + * encoding. Write conversion can convert strings twice... In case + * conversion from encoding X to encoding Y does not exist, Ruby finds an + * encoding Z that bridges the two, so that X to Z to Y conversion happens. + */ + RBIMPL_ATTR_DEPRECATED(("with no replacement")) + VALUE writeconv_asciicompat; + + /** Whether ::rb_io_t::writeconv is already set up. */ + RBIMPL_ATTR_DEPRECATED(("with no replacement")) + int writeconv_initialized; + + /** + * Value of ::rb_io_t::rb_io_enc_t::ecflags stored right before + * initialising ::rb_io_t::writeconv. + */ + RBIMPL_ATTR_DEPRECATED(("with no replacement")) + int writeconv_pre_ecflags; + + /** + * Value of ::rb_io_t::rb_io_enc_t::ecopts stored right before initialising + * ::rb_io_t::writeconv. + */ + RBIMPL_ATTR_DEPRECATED(("with no replacement")) + VALUE writeconv_pre_ecopts; + + /** + * This is a Ruby level mutex. It avoids multiple threads to write to an + * IO at once; helps for instance rb_io_puts() to ensure newlines right + * next to its arguments. + * + * This of course doesn't help inter-process IO interleaves, though. + */ + RBIMPL_ATTR_DEPRECATED(("with no replacement")) + VALUE write_lock; + + /** + * The timeout associated with this IO when performing blocking operations. + */ + RBIMPL_ATTR_DEPRECATED(("rb_io_timeout/rb_io_set_timeout")) + VALUE timeout; +}; +#endif + +typedef struct rb_io rb_io_t; + +/** @alias{rb_io_enc_t} */ +typedef struct rb_io_encoding rb_io_enc_t; + +/** + * Allocate a new IO object, with the given file descriptor. + */ +VALUE rb_io_open_descriptor(VALUE klass, int descriptor, int mode, VALUE path, VALUE timeout, struct rb_io_encoding *encoding); + +/** + * Returns whether or not the underlying IO is closed. + * + * @return Whether the underlying IO is closed. + */ +VALUE rb_io_closed_p(VALUE io); + /** * Queries the underlying IO pointer. * @@ -401,14 +494,14 @@ rb_io_t *rb_io_make_open_file(VALUE obj); * like this: * * ```CXX - * typedef struct rb_io_t { + * typedef struct rb_io { * FILE *f; // stdio ptr for read/write * FILE *f2; // additional ptr for rw pipes * int mode; // mode flags * int pid; // child's pid (for pipes) * int lineno; // number of lines read * char *path; // pathname for file - * void (*finalize) _((struct rb_io_t*,int)); // finalize proc + * void (*finalize) _((struct rb_io*,int)); // finalize proc * } rb_io_t; *``` * @@ -472,7 +565,7 @@ FILE *rb_fdopen(int fd, const char *modestr); * * rb_io_modestr_fmode() is not a pure function because it raises. */ -int rb_io_modestr_fmode(const char *modestr); +enum rb_io_mode rb_io_modestr_fmode(const char *modestr); /** * Identical to rb_io_modestr_fmode(), except it returns a mixture of `O_` @@ -690,6 +783,12 @@ VALUE rb_io_set_write_io(VALUE io, VALUE w); void rb_io_set_nonblock(rb_io_t *fptr); /** + * Returns the path for the given IO. + * + */ +VALUE rb_io_path(VALUE io); + +/** * Returns an integer representing the numeric file descriptor for * <em>io</em>. * @@ -699,6 +798,12 @@ void rb_io_set_nonblock(rb_io_t *fptr); int rb_io_descriptor(VALUE io); /** + * Get the mode of the IO. + * + */ +int rb_io_mode(VALUE io); + +/** * This function breaks down the option hash that `IO#initialize` takes into * components. This is an implementation detail of rb_io_extract_modeenc() * today. People prefer that API instead. @@ -715,7 +820,7 @@ int rb_io_descriptor(VALUE io); * @post `enc2_p` is the specified external encoding. * @post `fmode_p` is the specified set of `FMODE_` modes. */ -int rb_io_extract_encoding_option(VALUE opt, rb_encoding **enc_p, rb_encoding **enc2_p, int *fmode_p); +int rb_io_extract_encoding_option(VALUE opt, rb_encoding **enc_p, rb_encoding **enc2_p, enum rb_io_mode *fmode_p); /** * This function can be seen as an extended version of @@ -784,7 +889,7 @@ int rb_io_extract_encoding_option(VALUE opt, rb_encoding **enc_p, rb_encoding ** * ) -> void * ``` */ -void rb_io_extract_modeenc(VALUE *vmode_p, VALUE *vperm_p, VALUE opthash, int *oflags_p, int *fmode_p, rb_io_enc_t *convconfig_p); +void rb_io_extract_modeenc(VALUE *vmode_p, VALUE *vperm_p, VALUE opthash, int *oflags_p, enum rb_io_mode *fmode_p, rb_io_enc_t *convconfig_p); /* :TODO: can this function be __attribute__((warn_unused_result)) or not? */ /** @@ -845,13 +950,37 @@ int rb_io_wait_writable(int fd); int rb_wait_for_single_fd(int fd, int events, struct timeval *tv); /** + * Get the timeout associated with the specified io object. + * + * @param[in] io An IO object. + * @retval RUBY_Qnil There is no associated timeout. + * @retval Otherwise The timeout value. + */ +VALUE rb_io_timeout(VALUE io); + +/** + * Set the timeout associated with the specified io object. This timeout is + * used as a best effort timeout to prevent operations from blocking forever. + * + * @param[in] io An IO object. + * @param[in] timeout A timeout value. Must respond to #to_f. + * @ + */ +VALUE rb_io_set_timeout(VALUE io, VALUE timeout); + +/** * Blocks until the passed IO is ready for the passed events. The "events" * here is a Ruby level integer, which is an OR-ed value of `IO::READABLE`, * `IO::WRITable`, and `IO::PRIORITY`. * + * If timeout is `Qnil`, it will use the default timeout as given by + * `rb_io_timeout(io)`. + * * @param[in] io An IO object to wait. * @param[in] events See above. * @param[in] timeout Time, or numeric seconds since UNIX epoch. + * If Qnil, use the default timeout. If Qfalse + * or Qundef, wait forever. * @exception rb_eIOError `io` is not open. * @exception rb_eRangeError `timeout` is out of range. * @exception rb_eSystemCallError `select(2)` failed for some reason. @@ -875,6 +1004,9 @@ VALUE rb_io_wait(VALUE io, VALUE events, VALUE timeout); * } * ``` * + * On timeout, ::RUBY_Qfalse is returned. Unless you are specifically handling + * the timeouts, you should typically raise ::rb_eIOTimeoutError in this case. + * * @param[in] error System errno. * @param[in] io An IO object to wait. * @param[in] events An integer set of interests. @@ -883,19 +1015,19 @@ VALUE rb_io_wait(VALUE io, VALUE events, VALUE timeout); * @exception rb_eRangeError `timeout` is out of range. * @exception rb_eSystemCallError `select(2)` failed for some reason. * @retval RUBY_Qfalse Operation timed out. + * @retval RUBY_Qnil Operation failed for some other reason (errno). * @retval Otherwise Actual events reached. * - * @internal - * - * This function to return ::RUBY_Qfalse on timeout could be unintended. It - * seems timeout feature has some rough edge. */ VALUE rb_io_maybe_wait(int error, VALUE io, VALUE events, VALUE timeout); /** * Blocks until the passed IO is ready for reading, if that makes sense for the - * passed errno. This is a special case of rb_io_maybe_wait() that only - * concerns for reading. + * passed errno. This is a special case of rb_io_maybe_wait() that is + * only concerned with reading and handles the timeout. + * + * If you do not want the default timeout handling, consider using + * ::rb_io_maybe_wait directly. * * @param[in] error System errno. * @param[in] io An IO object to wait. @@ -903,20 +1035,18 @@ VALUE rb_io_maybe_wait(int error, VALUE io, VALUE events, VALUE timeout); * @exception rb_eIOError `io` is not open. * @exception rb_eRangeError `timeout` is out of range. * @exception rb_eSystemCallError `select(2)` failed for some reason. - * @exception rb_eTypeError Operation timed out. - * @return Always returns ::RUBY_IO_READABLE. - * - * @internal - * - * Because rb_io_maybe_wait() returns ::RUBY_Qfalse on timeout, this function - * fails to convert that value to `int`, and raises ::rb_eTypeError. + * @exception rb_eIOTimeoutError The wait operation timed out. + * @retval Otherwise Always returns ::RUBY_IO_READABLE. */ int rb_io_maybe_wait_readable(int error, VALUE io, VALUE timeout); /** * Blocks until the passed IO is ready for writing, if that makes sense for the - * passed errno. This is a special case of rb_io_maybe_wait() that only - * concernsfor writing. + * passed errno. This is a special case of rb_io_maybe_wait() that is + * only concerned with writing, and handles the timeout. + * + * If you do not want the default timeout handling, consider using + * ::rb_io_maybe_wait directly. * * @param[in] error System errno. * @param[in] io An IO object to wait. @@ -924,13 +1054,8 @@ int rb_io_maybe_wait_readable(int error, VALUE io, VALUE timeout); * @exception rb_eIOError `io` is not open. * @exception rb_eRangeError `timeout` is out of range. * @exception rb_eSystemCallError `select(2)` failed for some reason. - * @exception rb_eTypeError Operation timed out. - * @return Always returns ::RUBY_IO_WRITABLE. - * - * @internal - * - * Because rb_io_maybe_wait() returns ::RUBY_Qfalse on timeout, this function - * fails to convert that value to `int`, and raises ::rb_eTypeError. + * @exception rb_eIOTimeoutError The wait operation timed out. + * @retval Otherwise Always returns ::RUBY_IO_WRITABLE. */ int rb_io_maybe_wait_writable(int error, VALUE io, VALUE timeout); @@ -988,6 +1113,18 @@ int rb_io_read_pending(rb_io_t *fptr); */ VALUE rb_stat_new(const struct stat *st); +#if RUBY_USE_STATX +/** + * Constructs an instance of ::rb_cStat from the passed information. + * + * @param[in] st A stat. + * @return Allocated new instance of ::rb_cStat. + */ +VALUE rb_statx_new(const rb_io_stat_data *st); +#else +# define rb_statx_new rb_stat_new +#endif + /* gc.c */ RBIMPL_SYMBOL_EXPORT_END() |
