From a55accd736e13307f7e925af86751ca878be2058 Mon Sep 17 00:00:00 2001 From: ksss Date: Mon, 23 Nov 2015 21:58:38 +0900 Subject: Implement IO.pipe --- README.md | 2 +- mrblib/io.rb | 13 +++++++++++ src/io.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++ test/io.rb | 75 ++++++++++++++++++++++++++++++++++++++++++------------------ 4 files changed, 130 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index d7fa6945b..71f3f5090 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ IO, File module for mruby | IO.copy_stream | | | | IO.new, IO.for_fd, IO.open | o | | | IO.foreach | | | -| IO.pipe | | | +| IO.pipe | o | | | IO.popen | o | | | IO.read | o | | | IO.readlines | | | diff --git a/mrblib/io.rb b/mrblib/io.rb index 4d2fda5f3..1742fac32 100644 --- a/mrblib/io.rb +++ b/mrblib/io.rb @@ -41,6 +41,19 @@ class IO end end + def self.pipe(&block) + if block + begin + r, w = IO._pipe + yield r, w + ensure + r.close unless r.closed? + w.close unless w.closed? + end + else + IO._pipe + end + end def self.read(path, length=nil, offset=nil, opt=nil) if not opt.nil? # 4 arguments diff --git a/src/io.c b/src/io.c index bcc6ea64e..abe7f85c4 100644 --- a/src/io.c +++ b/src/io.c @@ -149,6 +149,32 @@ mrb_fd_cloexec(mrb_state *mrb, int fd) #endif } +static int +mrb_cloexec_pipe(mrb_state *mrb, int fildes[2]) +{ + int ret; + ret = pipe(fildes); + if (ret == -1) + return -1; + mrb_fd_cloexec(mrb, fildes[0]); + mrb_fd_cloexec(mrb, fildes[1]); + return ret; +} + +static int +mrb_pipe(mrb_state *mrb, int pipes[2]) +{ + int ret; + ret = mrb_cloexec_pipe(mrb, pipes); + if (ret == -1) { + if (errno == EMFILE || errno == ENFILE) { + mrb_garbage_collect(mrb); + ret = mrb_cloexec_pipe(mrb, pipes); + } + } + return ret; +} + #ifndef _WIN32 static int mrb_proc_exec(const char *pname) @@ -644,6 +670,42 @@ mrb_io_read_data_pending(mrb_state *mrb, mrb_value io) return 0; } +static mrb_value +mrb_io_s_pipe(mrb_state *mrb, mrb_value klass) +{ + mrb_value r = mrb_nil_value(); + mrb_value w = mrb_nil_value(); + struct mrb_io *fptr_r; + struct mrb_io *fptr_w; + int pipes[2]; + + if (mrb_pipe(mrb, pipes) == -1) { + mrb_sys_fail(mrb, "pipe"); + } + + r = mrb_obj_value(mrb_data_object_alloc(mrb, mrb_class_ptr(klass), NULL, &mrb_io_type)); + mrb_iv_set(mrb, r, mrb_intern_cstr(mrb, "@buf"), mrb_str_new_cstr(mrb, "")); + mrb_iv_set(mrb, r, mrb_intern_cstr(mrb, "@pos"), mrb_fixnum_value(0)); + fptr_r = mrb_io_alloc(mrb); + fptr_r->fd = pipes[0]; + fptr_r->writable = 0; + fptr_r->sync = 0; + DATA_TYPE(r) = &mrb_io_type; + DATA_PTR(r) = fptr_r; + + w = mrb_obj_value(mrb_data_object_alloc(mrb, mrb_class_ptr(klass), NULL, &mrb_io_type)); + mrb_iv_set(mrb, w, mrb_intern_cstr(mrb, "@buf"), mrb_str_new_cstr(mrb, "")); + mrb_iv_set(mrb, w, mrb_intern_cstr(mrb, "@pos"), mrb_fixnum_value(0)); + fptr_w = mrb_io_alloc(mrb); + fptr_w->fd = pipes[1]; + fptr_w->writable = 1; + fptr_w->sync = 1; + DATA_TYPE(w) = &mrb_io_type; + DATA_PTR(w) = fptr_w; + + return mrb_assoc_new(mrb, r, w); +} + static mrb_value mrb_io_s_select(mrb_state *mrb, mrb_value klass) { @@ -925,6 +987,7 @@ mrb_init_io(mrb_state *mrb) mrb_define_class_method(mrb, io, "for_fd", mrb_io_s_for_fd, MRB_ARGS_ANY()); mrb_define_class_method(mrb, io, "select", mrb_io_s_select, MRB_ARGS_ANY()); mrb_define_class_method(mrb, io, "sysopen", mrb_io_s_sysopen, MRB_ARGS_ANY()); + mrb_define_class_method(mrb, io, "_pipe", mrb_io_s_pipe, MRB_ARGS_NONE()); mrb_define_method(mrb, io, "initialize", mrb_io_initialize, MRB_ARGS_ANY()); /* 15.2.20.5.21 (x)*/ mrb_define_method(mrb, io, "sync", mrb_io_sync, MRB_ARGS_NONE()); diff --git a/test/io.rb b/test/io.rb index 434607959..a35d7a074 100644 --- a/test/io.rb +++ b/test/io.rb @@ -402,28 +402,23 @@ assert('IO#close_on_exec') do io.close io.closed? - # # Use below when IO.pipe is implemented. - # begin - # r, w = IO.pipe - # assert_equal(false, r.close_on_exec?) - # r.close_on_exec = true - # assert_equal(true, r.close_on_exec?) - # r.close_on_exec = false - # assert_equal(false, r.close_on_exec?) - # r.close_on_exec = true - # assert_equal(true, r.close_on_exec?) - - # assert_equal(false, w.close_on_exec?) - # w.close_on_exec = true - # assert_equal(true, w.close_on_exec?) - # w.close_on_exec = false - # assert_equal(false, w.close_on_exec?) - # w.close_on_exec = true - # assert_equal(true, w.close_on_exec?) - # ensure - # r.close unless r.closed? - # w.close unless w.closed? - # end + begin + r, w = IO.pipe + assert_equal(true, r.close_on_exec?) + r.close_on_exec = false + assert_equal(false, r.close_on_exec?) + r.close_on_exec = true + assert_equal(true, r.close_on_exec?) + + assert_equal(true, w.close_on_exec?) + w.close_on_exec = false + assert_equal(false, w.close_on_exec?) + w.close_on_exec = true + assert_equal(true, w.close_on_exec?) + ensure + r.close unless r.closed? + w.close unless w.closed? + end end assert('IO#sysseek') do @@ -434,6 +429,42 @@ assert('IO#sysseek') do end end +assert('IO.pipe') do + called = false + IO.pipe do |r, w| + assert_true r.kind_of?(IO) + assert_true w.kind_of?(IO) + assert_false r.closed? + assert_false w.closed? + assert_true FileTest.pipe?(r) + assert_true FileTest.pipe?(w) + assert_nil r.pid + assert_nil w.pid + assert_true 2 < r.fileno + assert_true 2 < w.fileno + assert_true r.fileno != w.fileno + assert_false r.sync + assert_true w.sync + assert_equal 8, w.write('test for') + assert_equal 'test', r.read(4) + assert_equal ' for', r.read(4) + assert_equal 5, w.write(' pipe') + assert_equal nil, w.close + assert_equal ' pipe', r.read + called = true + assert_raise(IOError) { r.write 'test' } + # TODO: + # This assert expect raise IOError but got RuntimeError + # Because mruby-io not have flag for I/O readable + # assert_raise(IOError) { w.read } + end + assert_true called + + assert_nothing_raised do + IO.pipe { |r, w| r.close; w.close } + end +end + assert('`cmd`') do assert_equal `echo foo`, "foo\n" end -- cgit v1.2.3