summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--README.md2
-rw-r--r--mrblib/io.rb13
-rw-r--r--src/io.c63
-rw-r--r--test/io.rb75
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)
@@ -645,6 +671,42 @@ mrb_io_read_data_pending(mrb_state *mrb, mrb_value io)
}
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)
{
mrb_value *argv;
@@ -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