aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog30
-rw-r--r--common.mk1
-rw-r--r--file.c33
-rw-r--r--internal.h1
-rw-r--r--ruby.c57
-rw-r--r--test/ruby/test_require.rb35
6 files changed, 134 insertions, 23 deletions
diff --git a/ChangeLog b/ChangeLog
index f05edfd676..9385aa9457 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,33 @@
+Sun Oct 18 05:11:22 2015 KOSAKI Motohiro <kosaki.motohiro@gmail.com>
+
+ * ruby.c (open_load_file): reset O_NONBLOCK after open.
+ Even if S_ISREG() is true, the file may be file on FUSE filesystem
+ or something. We can't assume O_NONBLOCK is safe.
+ Moreover, we should wait if the path is point to FIFO. That's
+ FIFO semantics. GVL should be transparent from ruby script.
+ Thus, just reopen without O_NONBLOCK for filling the requirements.
+ [Bug #11060][Bug #11559]
+
+ * ruby.c (loadopen_func): new for the above.
+
+ * file.c (ruby_is_fd_loadable): new. for checks loadable file type
+ of not.
+ * file.c (rb_file_load_ok): use ruby_is_fd_loadble()
+ * internal.h: add ruby_is_fd_loadble()
+
+ * common.mk: now, ruby.o depend on thread.h.
+
+ * test/ruby/test_require.rb
+ (TestRequire#test_loading_fifo_threading_success): new test.
+ This test successful case that loading from FIFO.
+
+ * test/ruby/test_require.rb
+ (TestRequire#test_loading_fifo_threading_raise): rename from
+ test_loading_fifo_threading. You souldn't rescue an exception
+ if you test raise or not.
+ Moreover, this case should be caught IOError because load(FIFO)
+ should be blocked until given any input.
+
Sat Oct 17 13:55:32 2015 Nobuyoshi Nakada <nobu@ruby-lang.org>
* file.c (rb_file_expand_path_internal): concatenate converted
diff --git a/common.mk b/common.mk
index 2f152d59f5..cf4dca025d 100644
--- a/common.mk
+++ b/common.mk
@@ -2062,6 +2062,7 @@ ruby.$(OBJEXT): {$(VPATH)}ruby.c
ruby.$(OBJEXT): {$(VPATH)}ruby_atomic.h
ruby.$(OBJEXT): {$(VPATH)}st.h
ruby.$(OBJEXT): {$(VPATH)}subst.h
+ruby.$(OBJEXT): {$(VPATH)}thread.h
ruby.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
ruby.$(OBJEXT): {$(VPATH)}thread_native.h
ruby.$(OBJEXT): {$(VPATH)}util.h
diff --git a/file.c b/file.c
index 15ce4c1f4e..6f3a4378fd 100644
--- a/file.c
+++ b/file.c
@@ -5670,11 +5670,35 @@ rb_path_check(const char *path)
return 1;
}
+int
+ruby_is_fd_loadable(int fd)
+{
+#ifdef _WIN32
+ return 1;
+#else
+ struct stat st;
+
+ if (fstat(fd, &st) < 0)
+ return 0;
+
+ if (S_ISREG(st.st_mode))
+ return 1;
+ if (S_ISFIFO(st.st_mode))
+ return 1;
+
+ return 0;
+#endif
+}
+
#ifndef _WIN32
int
rb_file_load_ok(const char *path)
{
int ret = 1;
+ /*
+ open(2) may block if path is FIFO and it's empty. Let's use O_NONBLOCK.
+ FIXME: Why O_NDELAY is checked?
+ */
int mode = (O_RDONLY |
#if defined O_NONBLOCK
O_NONBLOCK |
@@ -5685,14 +5709,7 @@ rb_file_load_ok(const char *path)
int fd = rb_cloexec_open(path, mode, 0);
if (fd == -1) return 0;
rb_update_max_fd(fd);
-#if !defined DOSISH
- {
- struct stat st;
- if (fstat(fd, &st) || S_ISDIR(st.st_mode)) {
- ret = 0;
- }
- }
-#endif
+ ret = ruby_is_fd_loadable(fd);
(void)close(fd);
return ret;
}
diff --git a/internal.h b/internal.h
index 8f7ab96cd8..6941586703 100644
--- a/internal.h
+++ b/internal.h
@@ -762,6 +762,7 @@ VALUE rb_file_expand_path_internal(VALUE, VALUE, int, int, VALUE);
VALUE rb_get_path_check_to_string(VALUE, int);
VALUE rb_get_path_check_convert(VALUE, VALUE, int);
void Init_File(void);
+int ruby_is_fd_loadable(int fd);
#ifdef RUBY_FUNCTION_NAME_STRING
# if defined __GNUC__ && __GNUC__ >= 4
diff --git a/ruby.c b/ruby.c
index 044cf0d951..16abaafd40 100644
--- a/ruby.c
+++ b/ruby.c
@@ -16,6 +16,7 @@
#include <sys/cygwin.h>
#endif
#include "internal.h"
+#include "ruby/thread.h"
#include "eval_intern.h"
#include "dln.h"
#include <stdio.h>
@@ -1725,21 +1726,36 @@ load_file_internal(VALUE argp_v)
return (VALUE)tree;
}
+static void *
+loadopen_func(void *arg)
+{
+ int fd;
+ fd = rb_cloexec_open((const char *)arg, O_RDONLY, 0);
+ if (fd >= 0)
+ rb_update_max_fd(fd);
+
+ return (void *)(VALUE)fd;
+}
+
static VALUE
open_load_file(VALUE fname_v, int *xflag)
{
const char *fname = StringValueCStr(fname_v);
VALUE f;
+ int e;
if (RSTRING_LEN(fname_v) == 1 && fname[0] == '-') {
f = rb_stdin;
}
else {
- int fd, mode = O_RDONLY;
-#if defined O_NONBLOCK && !(O_NONBLOCK & O_ACCMODE)
+ int fd;
+ /* open(2) may block if fname is point to FIFO and it's empty. Let's
+ use O_NONBLOCK. */
+ int mode = O_RDONLY;
+#if defined O_NONBLOCK && HAVE_FCNTL && !(O_NONBLOCK & O_ACCMODE)
/* TODO: fix conflicting O_NONBLOCK in ruby/win32.h */
mode |= O_NONBLOCK;
-#elif defined O_NDELAY && !(O_NDELAY & O_ACCMODE)
+#elif defined O_NDELAY && HAVE_FCNTL && !(O_NDELAY & O_ACCMODE)
mod |= O_NDELAY;
#endif
#if defined DOSISH || defined __CYGWIN__
@@ -1751,21 +1767,44 @@ open_load_file(VALUE fname_v, int *xflag)
}
}
#endif
+
if ((fd = rb_cloexec_open(fname, mode, 0)) < 0) {
rb_load_fail(fname_v, strerror(errno));
}
- rb_update_max_fd(fd);
-#if !defined DOSISH && !defined __CYGWIN__
+ rb_update_max_fd(fd);
+
+#ifdef HAVE_FCNTL
+ /* disabling O_NONBLOCK */
+ if (fcntl(fd, F_SETFL, 0) < 0) {
+ e = errno;
+ close(fd);
+ rb_load_fail(fname_v, strerror(e));
+ }
+#endif
+
+#ifdef S_ISFIFO
{
struct stat st;
- int e;
- if ((fstat(fd, &st) != 0) && (e = errno, 1) ||
- (S_ISDIR(st.st_mode) && (e = EISDIR, 1))) {
- (void)close(fd);
+ if (fstat(fd, &st) != 0) {
+ e = errno;
+ close(fd);
rb_load_fail(fname_v, strerror(e));
}
+ if (S_ISFIFO(st.st_mode)) {
+ /* We need to wait if FIFO is empty. So, let's reopen it. */
+ close(fd);
+ fd = (int)(VALUE)rb_thread_call_without_gvl(loadopen_func,
+ (void *)fname, RUBY_UBF_IO, 0);
+ if (fd < 0)
+ rb_load_fail(fname_v, strerror(errno));
+ }
}
#endif
+ if (!ruby_is_fd_loadable(fd)) {
+ close(fd);
+ rb_load_fail(fname_v, strerror(errno));
+ }
+
f = rb_io_fdopen(fd, mode, fname);
}
return f;
diff --git a/test/ruby/test_require.rb b/test/ruby/test_require.rb
index d51aeb2ae7..947b285582 100644
--- a/test/ruby/test_require.rb
+++ b/test/ruby/test_require.rb
@@ -694,7 +694,7 @@ class TestRequire < Test::Unit::TestCase
}
end
- def test_loading_fifo_threading
+ def test_loading_fifo_threading_raise
Tempfile.create(%w'fifo .rb') {|f|
f.close
File.unlink(f.path)
@@ -702,16 +702,39 @@ class TestRequire < Test::Unit::TestCase
assert_separately(["-", f.path], <<-END, timeout: 3)
th = Thread.current
Thread.start {begin sleep(0.001) end until th.stop?; th.raise(IOError)}
- assert_nothing_raised do
- begin
- load(ARGV[0])
- rescue IOError
- end
+ assert_raise(IOError) do
+ load(ARGV[0])
end
END
}
end unless /mswin|mingw/ =~ RUBY_PLATFORM
+ def test_loading_fifo_threading_success
+ Tempfile.create(%w'fifo .rb') {|f|
+ f.close
+ File.unlink(f.path)
+ File.mkfifo(f.path)
+
+ assert_separately(["-", f.path], <<-INPUT, timeout: 3)
+ path = ARGV[0]
+ th = Thread.current
+ Thread.start {
+ begin
+ sleep(0.001)
+ end until th.stop?
+ open(path, File::WRONLY | File::NONBLOCK) {|fifo_w|
+ fifo_w.puts "class C1; FOO='foo'; end"
+ }
+ }
+
+ load(path)
+ assert_equal(C1::FOO, "foo")
+ INPUT
+ }
+
+ end unless /mswin|mingw/ =~ RUBY_PLATFORM
+
+
def test_throw_while_loading
Tempfile.create(%w'bug-11404 .rb') do |f|
f.puts 'sleep'