aboutsummaryrefslogtreecommitdiffstats
path: root/io.c
diff options
context:
space:
mode:
authorSamuel Williams <samuel.williams@oriontransfer.co.nz>2023-05-30 10:02:40 +0900
committerGitHub <noreply@github.com>2023-05-30 10:02:40 +0900
commit18e55fc1e1ec20e8f3166e3059e76c885fc9f8f2 (patch)
tree7e35773b3cd1ae50a0a94937df7fb175a3bed9ba /io.c
parent7ddcd0622f3275effa603c16934b0215cc8a542b (diff)
downloadruby-18e55fc1e1ec20e8f3166e3059e76c885fc9f8f2.tar.gz
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>
Diffstat (limited to 'io.c')
-rw-r--r--io.c142
1 files changed, 97 insertions, 45 deletions
diff --git a/io.c b/io.c
index 88cb2667f5..26c95d2d86 100644
--- a/io.c
+++ b/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;
@@ -9187,26 +9186,79 @@ stderr_getter(ID id, VALUE *ptr)
}
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);