summaryrefslogtreecommitdiff
path: root/ext/io/nonblock/nonblock.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/io/nonblock/nonblock.c')
-rw-r--r--ext/io/nonblock/nonblock.c132
1 files changed, 99 insertions, 33 deletions
diff --git a/ext/io/nonblock/nonblock.c b/ext/io/nonblock/nonblock.c
index 1c0bdc68e7..d90538f735 100644
--- a/ext/io/nonblock/nonblock.c
+++ b/ext/io/nonblock/nonblock.c
@@ -17,16 +17,27 @@
#endif
#include <fcntl.h>
+#ifndef HAVE_RB_IO_DESCRIPTOR
+static int
+io_descriptor_fallback(VALUE io)
+{
+ rb_io_t *fptr;
+ GetOpenFile(io, fptr);
+ return fptr->fd;
+}
+#define rb_io_descriptor io_descriptor_fallback
+#endif
+
#ifdef F_GETFL
static int
-io_nonblock_mode(int fd)
+get_fcntl_flags(int fd)
{
int f = fcntl(fd, F_GETFL);
if (f == -1) rb_sys_fail(0);
return f;
}
#else
-#define io_nonblock_mode(fd) ((void)(fd), 0)
+#define get_fcntl_flags(fd) ((void)(fd), 0)
#endif
#ifdef F_GETFL
@@ -39,10 +50,8 @@ io_nonblock_mode(int fd)
static VALUE
rb_io_nonblock_p(VALUE io)
{
- rb_io_t *fptr;
- GetOpenFile(io, fptr);
- if (io_nonblock_mode(fptr->fd) & O_NONBLOCK)
- return Qtrue;
+ if (get_fcntl_flags(rb_io_descriptor(io)) & O_NONBLOCK)
+ return Qtrue;
return Qfalse;
}
#else
@@ -50,6 +59,15 @@ rb_io_nonblock_p(VALUE io)
#endif
#ifdef F_SETFL
+static void
+set_fcntl_flags(int fd, int f)
+{
+ if (fcntl(fd, F_SETFL, f) == -1)
+ rb_sys_fail(0);
+}
+
+#ifndef RUBY_IO_NONBLOCK_METHODS
+
static int
io_nonblock_set(int fd, int f, int nb)
{
@@ -63,8 +81,7 @@ io_nonblock_set(int fd, int f, int nb)
return 0;
f &= ~O_NONBLOCK;
}
- if (fcntl(fd, F_SETFL, f) == -1)
- rb_sys_fail(0);
+ set_fcntl_flags(fd, f);
return 1;
}
@@ -74,32 +91,77 @@ io_nonblock_set(int fd, int f, int nb)
*
* Enables non-blocking mode on a stream when set to
* +true+, and blocking mode when set to +false+.
+ *
+ * This method set or clear O_NONBLOCK flag for the file descriptor
+ * in <em>ios</em>.
+ *
+ * The behavior of most IO methods is not affected by this flag
+ * because they retry system calls to complete their task
+ * after EAGAIN and partial read/write.
+ * (An exception is IO#syswrite which doesn't retry.)
+ *
+ * This method can be used to clear non-blocking mode of standard I/O.
+ * Since nonblocking methods (read_nonblock, etc.) set non-blocking mode but
+ * they doesn't clear it, this method is usable as follows.
+ *
+ * END { STDOUT.nonblock = false }
+ * STDOUT.write_nonblock("foo")
+ *
+ * Since the flag is shared across processes and
+ * many non-Ruby commands doesn't expect standard I/O with non-blocking mode,
+ * it would be safe to clear the flag before Ruby program exits.
+ *
+ * For example following Ruby program leaves STDIN/STDOUT/STDER non-blocking mode.
+ * (STDIN, STDOUT and STDERR are connected to a terminal.
+ * So making one of them nonblocking-mode effects other two.)
+ * Thus cat command try to read from standard input and
+ * it causes "Resource temporarily unavailable" error (EAGAIN).
+ *
+ * % ruby -e '
+ * STDOUT.write_nonblock("foo\n")'; cat
+ * foo
+ * cat: -: Resource temporarily unavailable
+ *
+ * Clearing the flag makes the behavior of cat command normal.
+ * (cat command waits input from standard input.)
+ *
+ * % ruby -rio/nonblock -e '
+ * END { STDOUT.nonblock = false }
+ * STDOUT.write_nonblock("foo")
+ * '; cat
+ * foo
+ *
*/
static VALUE
-rb_io_nonblock_set(VALUE io, VALUE nb)
+rb_io_nonblock_set(VALUE self, VALUE value)
{
- rb_io_t *fptr;
- GetOpenFile(io, fptr);
- if (RTEST(nb))
- rb_io_set_nonblock(fptr);
- else
- io_nonblock_set(fptr->fd, io_nonblock_mode(fptr->fd), RTEST(nb));
- return io;
+ if (RTEST(value)) {
+ rb_io_t *fptr;
+ GetOpenFile(self, fptr);
+ rb_io_set_nonblock(fptr);
+ }
+ else {
+ int descriptor = rb_io_descriptor(self);
+ io_nonblock_set(descriptor, get_fcntl_flags(descriptor), RTEST(value));
+ }
+
+ return self;
}
+#endif /* RUBY_IO_NONBLOCK_METHODS */
+
static VALUE
io_nonblock_restore(VALUE arg)
{
int *restore = (int *)arg;
- if (fcntl(restore[0], F_SETFL, restore[1]) == -1)
- rb_sys_fail(0);
+ set_fcntl_flags(restore[0], restore[1]);
return Qnil;
}
/*
* call-seq:
- * io.nonblock {|io| } -> io
- * io.nonblock(boolean) {|io| } -> io
+ * io.nonblock {|io| } -> object
+ * io.nonblock(boolean) {|io| } -> object
*
* Yields +self+ in non-blocking mode.
*
@@ -107,24 +169,25 @@ io_nonblock_restore(VALUE arg)
* The original mode is restored after the block is executed.
*/
static VALUE
-rb_io_nonblock_block(int argc, VALUE *argv, VALUE io)
+rb_io_nonblock_block(int argc, VALUE *argv, VALUE self)
{
int nb = 1;
- rb_io_t *fptr;
- int f, restore[2];
- GetOpenFile(io, fptr);
+ int descriptor = rb_io_descriptor(self);
+
if (argc > 0) {
- VALUE v;
- rb_scan_args(argc, argv, "01", &v);
- nb = RTEST(v);
+ VALUE v;
+ rb_scan_args(argc, argv, "01", &v);
+ nb = RTEST(v);
}
- f = io_nonblock_mode(fptr->fd);
- restore[0] = fptr->fd;
- restore[1] = f;
- if (!io_nonblock_set(fptr->fd, f, nb))
- return rb_yield(io);
- return rb_ensure(rb_yield, io, io_nonblock_restore, (VALUE)restore);
+
+ int current_flags = get_fcntl_flags(descriptor);
+ int restore[2] = {descriptor, current_flags};
+
+ if (!io_nonblock_set(descriptor, current_flags, nb))
+ return rb_yield(self);
+
+ return rb_ensure(rb_yield, self, io_nonblock_restore, (VALUE)restore);
}
#else
#define rb_io_nonblock_set rb_f_notimplement
@@ -134,7 +197,10 @@ rb_io_nonblock_block(int argc, VALUE *argv, VALUE io)
void
Init_nonblock(void)
{
+#ifndef RUBY_IO_NONBLOCK_METHODS
rb_define_method(rb_cIO, "nonblock?", rb_io_nonblock_p, 0);
rb_define_method(rb_cIO, "nonblock=", rb_io_nonblock_set, 1);
+#endif
+
rb_define_method(rb_cIO, "nonblock", rb_io_nonblock_block, -1);
}