Hide most of the implementation of struct rb_io
. (#6511)
* Add rb_io_path and rb_io_open_descriptor. * Use rb_io_open_descriptor to create PTY objects * Rename FMODE_PREP -> FMODE_EXTERNAL and expose it FMODE_PREP I believe refers to the concept of a "pre-prepared" file, but FMODE_EXTERNAL is clearer about what the file descriptor represents and aligns with language in the IO::Buffer module. * Ensure that rb_io_open_descriptor closes the FD if it fails If FMODE_EXTERNAL is not set, then it's guaranteed that Ruby will be responsible for closing your file, eventually, if you pass it to rb_io_open_descriptor, even if it raises an exception. * Rename IS_EXTERNAL_FD -> RUBY_IO_EXTERNAL_P * Expose `rb_io_closed_p`. * Add `rb_io_mode` to get IO mode. --------- Co-authored-by: KJ Tsanaktsidis <ktsanaktsidis@zendesk.com>
This commit is contained in:
parent
7ddcd0622f
commit
18e55fc1e1
Notes:
git
2023-05-30 01:03:01 +00:00
Merged-By: ioquatix <samuel@codeotaku.com>
@ -10997,6 +10997,7 @@ process.$(OBJEXT): $(top_srcdir)/internal/fixnum.h
|
||||
process.$(OBJEXT): $(top_srcdir)/internal/gc.h
|
||||
process.$(OBJEXT): $(top_srcdir)/internal/hash.h
|
||||
process.$(OBJEXT): $(top_srcdir)/internal/imemo.h
|
||||
process.$(OBJEXT): $(top_srcdir)/internal/io.h
|
||||
process.$(OBJEXT): $(top_srcdir)/internal/numeric.h
|
||||
process.$(OBJEXT): $(top_srcdir)/internal/object.h
|
||||
process.$(OBJEXT): $(top_srcdir)/internal/process.h
|
||||
|
2
debug.c
2
debug.c
@ -70,7 +70,7 @@ const union {
|
||||
RUBY_FMODE_NOREVLOOKUP = 0x00000100,
|
||||
RUBY_FMODE_TRUNC = FMODE_TRUNC,
|
||||
RUBY_FMODE_TEXTMODE = FMODE_TEXTMODE,
|
||||
RUBY_FMODE_PREP = 0x00010000,
|
||||
RUBY_FMODE_EXTERNAL = 0x00010000,
|
||||
RUBY_FMODE_SETENC_BY_BOM = FMODE_SETENC_BY_BOM,
|
||||
RUBY_FMODE_UNIX = 0x00200000,
|
||||
RUBY_FMODE_INET = 0x00400000,
|
||||
|
@ -580,6 +580,7 @@ objspace_dump.o: $(top_srcdir)/internal/compilers.h
|
||||
objspace_dump.o: $(top_srcdir)/internal/gc.h
|
||||
objspace_dump.o: $(top_srcdir)/internal/hash.h
|
||||
objspace_dump.o: $(top_srcdir)/internal/imemo.h
|
||||
objspace_dump.o: $(top_srcdir)/internal/io.h
|
||||
objspace_dump.o: $(top_srcdir)/internal/sanitizers.h
|
||||
objspace_dump.o: $(top_srcdir)/internal/serial.h
|
||||
objspace_dump.o: $(top_srcdir)/internal/static_assert.h
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "internal/class.h"
|
||||
#include "internal/gc.h"
|
||||
#include "internal/hash.h"
|
||||
#include "internal/io.h"
|
||||
#include "internal/string.h"
|
||||
#include "internal/sanitizers.h"
|
||||
#include "symbol.h"
|
||||
|
@ -448,8 +448,10 @@ pty_close_pty(VALUE assoc)
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
io = rb_ary_entry(assoc, i);
|
||||
if (RB_TYPE_P(io, T_FILE) && 0 <= RFILE(io)->fptr->fd)
|
||||
if (RB_TYPE_P(io, T_FILE)) {
|
||||
/* it's OK to call rb_io_close again even if it's already closed */
|
||||
rb_io_close(io);
|
||||
}
|
||||
}
|
||||
return Qnil;
|
||||
}
|
||||
@ -499,28 +501,21 @@ pty_open(VALUE klass)
|
||||
{
|
||||
int master_fd, slave_fd;
|
||||
char slavename[DEVICELEN];
|
||||
VALUE master_io, slave_file;
|
||||
rb_io_t *master_fptr, *slave_fptr;
|
||||
VALUE assoc;
|
||||
|
||||
getDevice(&master_fd, &slave_fd, slavename, 1);
|
||||
|
||||
master_io = rb_obj_alloc(rb_cIO);
|
||||
MakeOpenFile(master_io, master_fptr);
|
||||
master_fptr->mode = FMODE_READWRITE | FMODE_SYNC | FMODE_DUPLEX;
|
||||
master_fptr->fd = master_fd;
|
||||
master_fptr->pathv = rb_obj_freeze(rb_sprintf("masterpty:%s", slavename));
|
||||
VALUE master_path = rb_obj_freeze(rb_sprintf("masterpty:%s", slavename));
|
||||
VALUE master_io = rb_io_open_descriptor(rb_cIO, master_fd, FMODE_READWRITE | FMODE_SYNC | FMODE_DUPLEX, master_path, RUBY_IO_TIMEOUT_DEFAULT, NULL);
|
||||
|
||||
slave_file = rb_obj_alloc(rb_cFile);
|
||||
MakeOpenFile(slave_file, slave_fptr);
|
||||
slave_fptr->mode = FMODE_READWRITE | FMODE_SYNC | FMODE_DUPLEX | FMODE_TTY;
|
||||
slave_fptr->fd = slave_fd;
|
||||
slave_fptr->pathv = rb_obj_freeze(rb_str_new_cstr(slavename));
|
||||
VALUE slave_path = rb_obj_freeze(rb_str_new_cstr(slavename));
|
||||
VALUE slave_file = rb_io_open_descriptor(rb_cFile, slave_fd, FMODE_READWRITE | FMODE_SYNC | FMODE_DUPLEX | FMODE_TTY, slave_path, RUBY_IO_TIMEOUT_DEFAULT, NULL);
|
||||
|
||||
VALUE assoc = rb_assoc_new(master_io, slave_file);
|
||||
|
||||
assoc = rb_assoc_new(master_io, slave_file);
|
||||
if (rb_block_given_p()) {
|
||||
return rb_ensure(rb_yield, assoc, pty_close_pty, assoc);
|
||||
}
|
||||
|
||||
return assoc;
|
||||
}
|
||||
|
||||
@ -577,30 +572,27 @@ pty_getpty(int argc, VALUE *argv, VALUE self)
|
||||
{
|
||||
VALUE res;
|
||||
struct pty_info info;
|
||||
rb_io_t *wfptr,*rfptr;
|
||||
VALUE rport = rb_obj_alloc(rb_cFile);
|
||||
VALUE wport = rb_obj_alloc(rb_cFile);
|
||||
char SlaveName[DEVICELEN];
|
||||
|
||||
MakeOpenFile(rport, rfptr);
|
||||
MakeOpenFile(wport, wfptr);
|
||||
|
||||
establishShell(argc, argv, &info, SlaveName);
|
||||
|
||||
rfptr->mode = rb_io_modestr_fmode("r");
|
||||
rfptr->fd = info.fd;
|
||||
rfptr->pathv = rb_obj_freeze(rb_str_new_cstr(SlaveName));
|
||||
VALUE pty_path = rb_obj_freeze(rb_str_new_cstr(SlaveName));
|
||||
VALUE rport = rb_io_open_descriptor(
|
||||
rb_cFile, info.fd, FMODE_READABLE, pty_path, RUBY_IO_TIMEOUT_DEFAULT, NULL
|
||||
);
|
||||
|
||||
wfptr->mode = rb_io_modestr_fmode("w") | FMODE_SYNC;
|
||||
wfptr->fd = rb_cloexec_dup(info.fd);
|
||||
if (wfptr->fd == -1)
|
||||
int wpty_fd = rb_cloexec_dup(info.fd);
|
||||
if (wpty_fd == -1) {
|
||||
rb_sys_fail("dup()");
|
||||
rb_update_max_fd(wfptr->fd);
|
||||
wfptr->pathv = rfptr->pathv;
|
||||
}
|
||||
VALUE wport = rb_io_open_descriptor(
|
||||
rb_cFile, wpty_fd, FMODE_WRITABLE | FMODE_TRUNC | FMODE_CREATE | FMODE_SYNC,
|
||||
pty_path, RUBY_IO_TIMEOUT_DEFAULT, NULL
|
||||
);
|
||||
|
||||
res = rb_ary_new2(3);
|
||||
rb_ary_store(res,0,(VALUE)rport);
|
||||
rb_ary_store(res,1,(VALUE)wport);
|
||||
rb_ary_store(res, 0, rport);
|
||||
rb_ary_store(res, 1, wport);
|
||||
rb_ary_store(res,2,PIDT2NUM(info.child_pid));
|
||||
|
||||
if (rb_block_given_p()) {
|
||||
|
@ -276,7 +276,7 @@ strio_init(int argc, VALUE *argv, struct StringIO *ptr, VALUE self)
|
||||
{
|
||||
VALUE string, vmode, opt;
|
||||
int oflags;
|
||||
struct rb_io_enc_t convconfig;
|
||||
struct rb_io_encoding convconfig;
|
||||
|
||||
argc = rb_scan_args(argc, argv, "02:", &string, &vmode, &opt);
|
||||
rb_io_extract_modeenc(&vmode, 0, opt, &oflags, &ptr->flags, &convconfig);
|
||||
@ -1743,7 +1743,7 @@ strio_set_encoding(int argc, VALUE *argv, VALUE self)
|
||||
else {
|
||||
enc = rb_find_encoding(ext_enc);
|
||||
if (!enc) {
|
||||
struct rb_io_enc_t convconfig;
|
||||
struct rb_io_encoding convconfig;
|
||||
int oflags, fmode;
|
||||
VALUE vmode = rb_str_append(rb_str_new_cstr("r:"), ext_enc);
|
||||
rb_io_extract_modeenc(&vmode, 0, Qnil, &oflags, &fmode, &convconfig);
|
||||
|
1
file.c
1
file.c
@ -169,7 +169,6 @@ int flock(int, int);
|
||||
#include "internal/thread.h"
|
||||
#include "internal/vm.h"
|
||||
#include "ruby/encoding.h"
|
||||
#include "ruby/io.h"
|
||||
#include "ruby/thread.h"
|
||||
#include "ruby/util.h"
|
||||
|
||||
|
@ -23,9 +23,10 @@
|
||||
#include "ruby/internal/core/rbasic.h"
|
||||
#include "ruby/internal/cast.h"
|
||||
|
||||
/* rb_io_t is in ruby/io.h. The header file has historically not been included
|
||||
* into ruby/ruby.h. We follow that tradition. */
|
||||
struct rb_io_t;
|
||||
/* rb_io is in ruby/io.h and internal/io.h. The header file has historically
|
||||
* not been included into ruby/ruby.h. We follow that tradition.
|
||||
*/
|
||||
struct rb_io;
|
||||
|
||||
/**
|
||||
* Ruby's File and IO. Ruby's IO are not just file descriptors. They have
|
||||
@ -38,7 +39,7 @@ struct RFile {
|
||||
struct RBasic basic;
|
||||
|
||||
/** IO's specific fields. */
|
||||
struct rb_io_t *fptr;
|
||||
struct rb_io *fptr;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -84,29 +84,6 @@ typedef enum {
|
||||
RUBY_IO_PRIORITY = RB_WAITFD_PRI, /**< `IO::PRIORITY` */
|
||||
} 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.
|
||||
*/
|
||||
RBIMPL_ATTR_PACKED_STRUCT_UNALIGNED_BEGIN()
|
||||
struct rb_io_buffer_t {
|
||||
|
||||
/** Pointer to the underlying memory region, of at least `capa` bytes. */
|
||||
char *ptr; /* off + len <= capa */
|
||||
|
||||
/** Offset inside of `ptr`. */
|
||||
int off;
|
||||
|
||||
/** Length of the buffer. */
|
||||
int len;
|
||||
|
||||
/** 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;
|
||||
|
||||
/** Decomposed encoding flags (e.g. `"enc:enc2""`). */
|
||||
/*
|
||||
* enc enc2 read action write action
|
||||
@ -114,7 +91,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. */
|
||||
@ -135,103 +112,10 @@ struct rb_io_enc_t {
|
||||
VALUE ecopts;
|
||||
};
|
||||
|
||||
/** Ruby's IO, metadata and buffers. */
|
||||
typedef struct rb_io_t {
|
||||
struct rb_io;
|
||||
typedef struct rb_io 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;
|
||||
|
||||
/**
|
||||
* The timeout associated with this IO when performing blocking operations.
|
||||
*/
|
||||
VALUE timeout;
|
||||
} rb_io_t;
|
||||
|
||||
/** @alias{rb_io_enc_t} */
|
||||
typedef struct rb_io_enc_t rb_io_enc_t;
|
||||
typedef struct rb_io_encoding rb_io_enc_t;
|
||||
|
||||
/**
|
||||
* @private
|
||||
@ -331,7 +215,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 */
|
||||
|
||||
/**
|
||||
@ -345,6 +238,18 @@ typedef struct rb_io_enc_t 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.
|
||||
*
|
||||
@ -703,6 +608,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>.
|
||||
@ -712,6 +623,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()
|
||||
|
119
internal/io.h
119
internal/io.h
@ -8,8 +8,125 @@
|
||||
* file COPYING are met. Consult the file for details.
|
||||
* @brief Internal header for IO.
|
||||
*/
|
||||
|
||||
#define RB_IO_T
|
||||
|
||||
#include "ruby/ruby.h" /* for VALUE */
|
||||
#include "ruby/io.h" /* for rb_io_t */
|
||||
#include "ruby/io.h"
|
||||
|
||||
/**
|
||||
* IO buffers. This is an implementation detail of ::rb_io_t::wbuf and
|
||||
* ::rb_io_t::rbuf. People don't manipulate it directly.
|
||||
*/
|
||||
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 */
|
||||
|
||||
/** Offset inside of `ptr`. */
|
||||
int off;
|
||||
|
||||
/** Length of the buffer. */
|
||||
int len;
|
||||
|
||||
/** Designed capacity of the buffer. */
|
||||
int capa;
|
||||
} RBIMPL_ATTR_PACKED_STRUCT_UNALIGNED_END();
|
||||
|
||||
/** Ruby's IO, metadata and buffers. */
|
||||
struct rb_io {
|
||||
/** 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;
|
||||
|
||||
/**
|
||||
* Duplex IO object, if set.
|
||||
*
|
||||
* @see rb_io_set_write_io()
|
||||
*/
|
||||
VALUE tied_io_for_writing;
|
||||
|
||||
/** finalize proc */
|
||||
void (*finalize)(struct rb_io*,int);
|
||||
|
||||
/** Write buffer. */
|
||||
struct rb_io_internal_buffer wbuf;
|
||||
|
||||
/**
|
||||
* (Byte) read buffer. Note also that there is a field called
|
||||
* ::rb_io_t::cbuf, which also concerns read IO.
|
||||
*/
|
||||
struct rb_io_internal_buffer rbuf;
|
||||
|
||||
struct rb_io_encoding 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
|
||||
*/
|
||||
struct rb_io_internal_buffer 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;
|
||||
|
||||
/**
|
||||
* The timeout associated with this IO when performing blocking operations.
|
||||
*/
|
||||
VALUE timeout;
|
||||
};
|
||||
|
||||
/* io.c */
|
||||
void ruby_set_inplace_mode(const char *);
|
||||
|
142
io.c
142
io.c
@ -16,11 +16,6 @@
|
||||
#include "ruby/fiber/scheduler.h"
|
||||
#include "ruby/io/buffer.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
# include "ruby/ruby.h"
|
||||
# include "ruby/io.h"
|
||||
#endif
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <stddef.h>
|
||||
@ -221,7 +216,7 @@ struct argf {
|
||||
long lineno;
|
||||
VALUE argv;
|
||||
VALUE inplace;
|
||||
struct rb_io_enc_t encs;
|
||||
struct rb_io_encoding encs;
|
||||
int8_t init_p, next_p, binmode;
|
||||
};
|
||||
|
||||
@ -525,7 +520,6 @@ rb_cloexec_fcntl_dupfd(int fd, int minfd)
|
||||
static int io_fflush(rb_io_t *);
|
||||
static rb_io_t *flush_before_seek(rb_io_t *fptr);
|
||||
|
||||
#define FMODE_PREP (1<<16)
|
||||
#define FMODE_SIGNAL_ON_EPIPE (1<<17)
|
||||
|
||||
#define fptr_signal_on_epipe(fptr) \
|
||||
@ -1463,7 +1457,7 @@ rb_io_wait(VALUE io, VALUE events, VALUE timeout)
|
||||
static VALUE
|
||||
io_from_fd(int fd)
|
||||
{
|
||||
return prep_io(fd, FMODE_PREP, rb_cIO, NULL);
|
||||
return prep_io(fd, FMODE_EXTERNAL, rb_cIO, NULL);
|
||||
}
|
||||
|
||||
static int
|
||||
@ -2875,6 +2869,13 @@ rb_io_descriptor(VALUE io)
|
||||
}
|
||||
}
|
||||
|
||||
int rb_io_mode(VALUE io)
|
||||
{
|
||||
rb_io_t *fptr;
|
||||
GetOpenFile(io, fptr);
|
||||
return fptr->mode;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* pid -> integer or nil
|
||||
@ -2921,7 +2922,7 @@ rb_io_pid(VALUE io)
|
||||
* File.open("testfile") {|f| f.path} # => "testfile"
|
||||
*/
|
||||
|
||||
static VALUE
|
||||
VALUE
|
||||
rb_io_path(VALUE io)
|
||||
{
|
||||
rb_io_t *fptr = RFILE(io)->fptr;
|
||||
@ -5305,7 +5306,7 @@ rb_io_set_close_on_exec(VALUE io, VALUE arg)
|
||||
#define rb_io_set_close_on_exec rb_f_notimplement
|
||||
#endif
|
||||
|
||||
#define IS_PREP_STDIO(f) ((f)->mode & FMODE_PREP)
|
||||
#define RUBY_IO_EXTERNAL_P(f) ((f)->mode & FMODE_EXTERNAL)
|
||||
#define PREP_STDIO_NAME(f) (RSTRING_PTR((f)->pathv))
|
||||
|
||||
static VALUE
|
||||
@ -5419,7 +5420,7 @@ maygvl_fclose(FILE *file, int keepgvl)
|
||||
return (int)(intptr_t)rb_thread_call_without_gvl(nogvl_fclose, file, RUBY_UBF_IO, 0);
|
||||
}
|
||||
|
||||
static void free_io_buffer(rb_io_buffer_t *buf);
|
||||
static void free_io_buffer(struct rb_io_internal_buffer *buf);
|
||||
static void clear_codeconv(rb_io_t *fptr);
|
||||
|
||||
static void*
|
||||
@ -5463,7 +5464,7 @@ fptr_finalize_flush(rb_io_t *fptr, int noraise, int keepgvl,
|
||||
|
||||
int done = 0;
|
||||
|
||||
if (IS_PREP_STDIO(fptr) || fd <= 2) {
|
||||
if (RUBY_IO_EXTERNAL_P(fptr) || fd <= 2) {
|
||||
// Need to keep FILE objects of stdin, stdout and stderr, so we are done:
|
||||
done = 1;
|
||||
}
|
||||
@ -5542,7 +5543,7 @@ rb_io_fptr_cleanup(rb_io_t *fptr, int noraise)
|
||||
}
|
||||
|
||||
static void
|
||||
free_io_buffer(rb_io_buffer_t *buf)
|
||||
free_io_buffer(struct rb_io_internal_buffer *buf)
|
||||
{
|
||||
if (buf->ptr) {
|
||||
ruby_sized_xfree(buf->ptr, (size_t)buf->capa);
|
||||
@ -5770,10 +5771,8 @@ io_close(VALUE io)
|
||||
*
|
||||
* Related: IO#close_read, IO#close_write, IO#close.
|
||||
*/
|
||||
|
||||
|
||||
static VALUE
|
||||
rb_io_closed(VALUE io)
|
||||
VALUE
|
||||
rb_io_closed_p(VALUE io)
|
||||
{
|
||||
rb_io_t *fptr;
|
||||
VALUE write_io;
|
||||
@ -6724,7 +6723,7 @@ rb_io_extract_encoding_option(VALUE opt, rb_encoding **enc_p, rb_encoding **enc2
|
||||
return extracted;
|
||||
}
|
||||
|
||||
typedef struct rb_io_enc_t convconfig_t;
|
||||
typedef struct rb_io_encoding convconfig_t;
|
||||
|
||||
static void
|
||||
validate_enc_binmode(int *fmode_p, int ecflags, rb_encoding *enc, rb_encoding *enc2)
|
||||
@ -7258,7 +7257,7 @@ static void
|
||||
fptr_copy_finalizer(rb_io_t *fptr, const rb_io_t *orig)
|
||||
{
|
||||
#if defined(__CYGWIN__) || !defined(HAVE_WORKING_FORK)
|
||||
void (*const old_finalize)(struct rb_io_t*,int) = fptr->finalize;
|
||||
void (*const old_finalize)(rb_io_t*,int) = fptr->finalize;
|
||||
|
||||
if (old_finalize == orig->finalize) return;
|
||||
#endif
|
||||
@ -8289,7 +8288,7 @@ io_reopen(VALUE io, VALUE nfile)
|
||||
GetOpenFile(nfile, orig);
|
||||
|
||||
if (fptr == orig) return io;
|
||||
if (IS_PREP_STDIO(fptr)) {
|
||||
if (RUBY_IO_EXTERNAL_P(fptr)) {
|
||||
if ((fptr->stdio_file == stdin && !(orig->mode & FMODE_READABLE)) ||
|
||||
(fptr->stdio_file == stdout && !(orig->mode & FMODE_WRITABLE)) ||
|
||||
(fptr->stdio_file == stderr && !(orig->mode & FMODE_WRITABLE))) {
|
||||
@ -8315,17 +8314,17 @@ io_reopen(VALUE io, VALUE nfile)
|
||||
}
|
||||
|
||||
/* copy rb_io_t structure */
|
||||
fptr->mode = orig->mode | (fptr->mode & FMODE_PREP);
|
||||
fptr->mode = orig->mode | (fptr->mode & FMODE_EXTERNAL);
|
||||
fptr->pid = orig->pid;
|
||||
fptr->lineno = orig->lineno;
|
||||
if (RTEST(orig->pathv)) fptr->pathv = orig->pathv;
|
||||
else if (!IS_PREP_STDIO(fptr)) fptr->pathv = Qnil;
|
||||
else if (!RUBY_IO_EXTERNAL_P(fptr)) fptr->pathv = Qnil;
|
||||
fptr_copy_finalizer(fptr, orig);
|
||||
|
||||
fd = fptr->fd;
|
||||
fd2 = orig->fd;
|
||||
if (fd != fd2) {
|
||||
if (IS_PREP_STDIO(fptr) || fd <= 2 || !fptr->stdio_file) {
|
||||
if (RUBY_IO_EXTERNAL_P(fptr) || fd <= 2 || !fptr->stdio_file) {
|
||||
/* need to keep FILE objects of stdin, stdout and stderr */
|
||||
if (rb_cloexec_dup2(fd2, fd) < 0)
|
||||
rb_sys_fail_path(orig->pathv);
|
||||
@ -8433,7 +8432,7 @@ rb_io_reopen(int argc, VALUE *argv, VALUE file)
|
||||
convconfig_t convconfig;
|
||||
|
||||
rb_io_extract_modeenc(&nmode, 0, opt, &oflags, &fmode, &convconfig);
|
||||
if (IS_PREP_STDIO(fptr) &&
|
||||
if (RUBY_IO_EXTERNAL_P(fptr) &&
|
||||
((fptr->mode & FMODE_READWRITE) & (fmode & FMODE_READWRITE)) !=
|
||||
(fptr->mode & FMODE_READWRITE)) {
|
||||
rb_raise(rb_eArgError,
|
||||
@ -8512,7 +8511,7 @@ rb_io_init_copy(VALUE dest, VALUE io)
|
||||
rb_io_flush(io);
|
||||
|
||||
/* copy rb_io_t structure */
|
||||
fptr->mode = orig->mode & ~FMODE_PREP;
|
||||
fptr->mode = orig->mode & ~FMODE_EXTERNAL;
|
||||
fptr->encs = orig->encs;
|
||||
fptr->pid = orig->pid;
|
||||
fptr->lineno = orig->lineno;
|
||||
@ -9186,27 +9185,80 @@ stderr_getter(ID id, VALUE *ptr)
|
||||
return rb_ractor_stderr();
|
||||
}
|
||||
|
||||
static VALUE
|
||||
allocate_and_open_new_file(VALUE klass)
|
||||
{
|
||||
VALUE self = io_alloc(klass);
|
||||
rb_io_make_open_file(self);
|
||||
return self;
|
||||
}
|
||||
|
||||
VALUE
|
||||
rb_io_open_descriptor(VALUE klass, int descriptor, int mode, VALUE path, VALUE timeout, struct rb_io_encoding *encoding)
|
||||
{
|
||||
|
||||
int state;
|
||||
VALUE self = rb_protect(allocate_and_open_new_file, klass, &state);
|
||||
if (state) {
|
||||
/* if we raised an exception allocating an IO object, but the caller
|
||||
intended to transfer ownership of this FD to us, close the fd before
|
||||
raising the exception. Otherwise, we would leak a FD - the caller
|
||||
expects GC to close the file, but we never got around to assigning
|
||||
it to a rb_io. */
|
||||
if (!(mode & FMODE_EXTERNAL)) {
|
||||
maygvl_close(descriptor, 0);
|
||||
}
|
||||
rb_jump_tag(state);
|
||||
}
|
||||
|
||||
|
||||
struct rb_io *io = RFILE(self)->fptr;
|
||||
io->self = self;
|
||||
io->fd = descriptor;
|
||||
io->mode = mode;
|
||||
|
||||
/* At this point, Ruby fully owns the descriptor, and will close it when
|
||||
the IO gets GC'd (unless FMODE_EXTERNAL was set), no matter what happens
|
||||
in the rest of this method. */
|
||||
|
||||
if (NIL_P(path)) {
|
||||
io->pathv = Qnil;
|
||||
}
|
||||
else {
|
||||
StringValue(path);
|
||||
io->pathv = rb_str_new_frozen(path);
|
||||
}
|
||||
|
||||
io->timeout = timeout;
|
||||
|
||||
if (encoding) {
|
||||
io->encs = *encoding;
|
||||
}
|
||||
|
||||
rb_update_max_fd(descriptor);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
prep_io(int fd, int fmode, VALUE klass, const char *path)
|
||||
{
|
||||
rb_io_t *fp;
|
||||
VALUE io = io_alloc(klass);
|
||||
VALUE path_value = Qnil;
|
||||
if (path) {
|
||||
path_value = rb_obj_freeze(rb_str_new_cstr(path));
|
||||
}
|
||||
|
||||
MakeOpenFile(io, fp);
|
||||
fp->self = io;
|
||||
fp->fd = fd;
|
||||
fp->mode = fmode;
|
||||
fp->timeout = Qnil;
|
||||
if (!io_check_tty(fp)) {
|
||||
VALUE self = rb_io_open_descriptor(klass, fd, fmode, path_value, Qnil, NULL);
|
||||
struct rb_io *io = RFILE(self)->fptr;
|
||||
|
||||
if (!io_check_tty(io)) {
|
||||
#ifdef __CYGWIN__
|
||||
fp->mode |= FMODE_BINMODE;
|
||||
io->mode |= FMODE_BINMODE;
|
||||
setmode(fd, O_BINARY);
|
||||
#endif
|
||||
}
|
||||
if (path) fp->pathv = rb_obj_freeze(rb_str_new_cstr(path));
|
||||
rb_update_max_fd(fd);
|
||||
|
||||
return io;
|
||||
return self;
|
||||
}
|
||||
|
||||
VALUE
|
||||
@ -9222,7 +9274,7 @@ static VALUE
|
||||
prep_stdio(FILE *f, int fmode, VALUE klass, const char *path)
|
||||
{
|
||||
rb_io_t *fptr;
|
||||
VALUE io = prep_io(fileno(f), fmode|FMODE_PREP|DEFAULT_TEXTMODE, klass, path);
|
||||
VALUE io = prep_io(fileno(f), fmode|FMODE_EXTERNAL|DEFAULT_TEXTMODE, klass, path);
|
||||
|
||||
GetOpenFile(io, fptr);
|
||||
fptr->encs.ecflags |= ECONV_DEFAULT_NEWLINE_DECORATOR;
|
||||
@ -9266,7 +9318,7 @@ rb_io_stdio_file(rb_io_t *fptr)
|
||||
}
|
||||
|
||||
static inline void
|
||||
rb_io_buffer_init(rb_io_buffer_t *buf)
|
||||
rb_io_buffer_init(struct rb_io_internal_buffer *buf)
|
||||
{
|
||||
buf->ptr = NULL;
|
||||
buf->off = 0;
|
||||
@ -9406,7 +9458,7 @@ rb_io_initialize(int argc, VALUE *argv, VALUE io)
|
||||
|
||||
if (!NIL_P(opt)) {
|
||||
if (rb_hash_aref(opt, sym_autoclose) == Qfalse) {
|
||||
fmode |= FMODE_PREP;
|
||||
fmode |= FMODE_EXTERNAL;
|
||||
}
|
||||
|
||||
path = rb_hash_aref(opt, RB_ID2SYM(idPath));
|
||||
@ -9583,7 +9635,7 @@ rb_io_autoclose_p(VALUE io)
|
||||
{
|
||||
rb_io_t *fptr = RFILE(io)->fptr;
|
||||
rb_io_check_closed(fptr);
|
||||
return RBOOL(!(fptr->mode & FMODE_PREP));
|
||||
return RBOOL(!(fptr->mode & FMODE_EXTERNAL));
|
||||
}
|
||||
|
||||
/*
|
||||
@ -9609,9 +9661,9 @@ rb_io_set_autoclose(VALUE io, VALUE autoclose)
|
||||
rb_io_t *fptr;
|
||||
GetOpenFile(io, fptr);
|
||||
if (!RTEST(autoclose))
|
||||
fptr->mode |= FMODE_PREP;
|
||||
fptr->mode |= FMODE_EXTERNAL;
|
||||
else
|
||||
fptr->mode &= ~FMODE_PREP;
|
||||
fptr->mode &= ~FMODE_EXTERNAL;
|
||||
return autoclose;
|
||||
}
|
||||
|
||||
@ -14442,7 +14494,7 @@ argf_closed(VALUE argf)
|
||||
{
|
||||
next_argv();
|
||||
ARGF_FORWARD(0, 0);
|
||||
return rb_io_closed(ARGF.current_file);
|
||||
return rb_io_closed_p(ARGF.current_file);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -15489,7 +15541,7 @@ Init_IO(void)
|
||||
rb_define_method(rb_cIO, "close_on_exec=", rb_io_set_close_on_exec, 1);
|
||||
|
||||
rb_define_method(rb_cIO, "close", rb_io_close_m, 0);
|
||||
rb_define_method(rb_cIO, "closed?", rb_io_closed, 0);
|
||||
rb_define_method(rb_cIO, "closed?", rb_io_closed_p, 0);
|
||||
rb_define_method(rb_cIO, "close_read", rb_io_close_read, 0);
|
||||
rb_define_method(rb_cIO, "close_write", rb_io_close_write, 0);
|
||||
|
||||
|
@ -103,6 +103,7 @@ int initgroups(const char *, rb_gid_t);
|
||||
#include "internal/error.h"
|
||||
#include "internal/eval.h"
|
||||
#include "internal/hash.h"
|
||||
#include "internal/io.h"
|
||||
#include "internal/numeric.h"
|
||||
#include "internal/object.h"
|
||||
#include "internal/process.h"
|
||||
|
@ -28,9 +28,13 @@ static int set_non_blocking(int fd) {
|
||||
}
|
||||
|
||||
static int io_spec_get_fd(VALUE io) {
|
||||
#ifdef RUBY_VERSION_IS_3_1
|
||||
return rb_io_descriptor(io);
|
||||
#else
|
||||
rb_io_t* fp;
|
||||
GetOpenFile(io, fp);
|
||||
return fp->fd;
|
||||
#endif
|
||||
}
|
||||
|
||||
VALUE io_spec_GetOpenFile_fd(VALUE self, VALUE io) {
|
||||
@ -303,7 +307,7 @@ VALUE io_spec_rb_io_set_nonblock(VALUE self, VALUE io) {
|
||||
GetOpenFile(io, fp);
|
||||
rb_io_set_nonblock(fp);
|
||||
#ifdef F_GETFL
|
||||
flags = fcntl(fp->fd, F_GETFL, 0);
|
||||
flags = fcntl(io_spec_get_fd(io), F_GETFL, 0);
|
||||
return flags & O_NONBLOCK ? Qtrue : Qfalse;
|
||||
#else
|
||||
return Qfalse;
|
||||
@ -322,9 +326,13 @@ static VALUE io_spec_errno_set(VALUE self, VALUE val) {
|
||||
}
|
||||
|
||||
VALUE io_spec_mode_sync_flag(VALUE self, VALUE io) {
|
||||
#ifdef RUBY_VERSION_IS_3_3
|
||||
if (rb_io_mode(io) & FMODE_SYNC) {
|
||||
#else
|
||||
rb_io_t *fp;
|
||||
GetOpenFile(io, fp);
|
||||
if (fp->mode & FMODE_SYNC) {
|
||||
#endif
|
||||
return Qtrue;
|
||||
} else {
|
||||
return Qfalse;
|
||||
|
@ -8269,10 +8269,7 @@ w32_io_info(VALUE *file, w32_io_info_t *st)
|
||||
|
||||
tmp = rb_check_convert_type_with_id(*file, T_FILE, "IO", idTo_io);
|
||||
if (!NIL_P(tmp)) {
|
||||
rb_io_t *fptr;
|
||||
|
||||
GetOpenFile(tmp, fptr);
|
||||
f = (HANDLE)rb_w32_get_osfhandle(fptr->fd);
|
||||
f = (HANDLE)rb_w32_get_osfhandle(rb_io_descriptor(tmp));
|
||||
if (f == (HANDLE)-1) return INVALID_HANDLE_VALUE;
|
||||
}
|
||||
else {
|
||||
|
Loading…
x
Reference in New Issue
Block a user