[#35631] [Ruby 1.9 - Bug #4558][Open] TestSocket#test_closed_read fails after r31230 — Tomoyuki Chikanaga <redmine@...>

23 messages 2011/04/06

[#35632] [Ruby 1.9 - Bug #4559][Open] Proc#== does not match the documented behaviour — Adam Prescott <redmine@...>

13 messages 2011/04/06

[#35637] [Ruby 1.9 - Bug #4561][Open] 1.9.2 requires parentheses around argument of method call in an array, where 1.8.7 did not — Dave Schweisguth <redmine@...>

9 messages 2011/04/07

[#35666] caching of the ancestor chain — Xavier Noria <fxn@...>

Why does Ruby cache the ancestors chain? I mean, not why the implementation implies that, but why it works that way conceptually.

9 messages 2011/04/09

[#35734] [Ruby 1.9 - Feature #4574][Open] Numeric#within — redmine@...

16 messages 2011/04/13

[#35753] [Ruby 1.9 - Bug #4576][Open] Range#step miss the last value, if end-exclusive and has float number — redmine@...

61 messages 2011/04/14
[#39566] [Ruby 1.9 - Bug #4576] Range#step miss the last value, if end-exclusive and has float number — Marc-Andre Lafortune <ruby-core@...> 2011/09/15

[#39590] [Ruby 1.9 - Bug #4576] Range#step miss the last value, if end-exclusive and has float number — Marc-Andre Lafortune <ruby-core@...> 2011/09/16

[#39593] Re: [Ruby 1.9 - Bug #4576] Range#step miss the last value, if end-exclusive and has float number — Tanaka Akira <akr@...> 2011/09/16

2011/9/17 Marc-Andre Lafortune <ruby-core@marc-andre.ca>:

[#39608] Re: [Ruby 1.9 - Bug #4576] Range#step miss the last value, if end-exclusive and has float number — Masahiro TANAKA <masa16.tanaka@...> 2011/09/17

I have not been watching ruby-core, but let me give a comment for this issue.

[#35765] [Ruby 1.9 - Bug #4579][Open] SecureRandom + OpenSSL may repeat with fork — redmine@...

27 messages 2011/04/15

[#35866] [Ruby 1.9 - Bug #4603][Open] lib/csv.rb: when the :encoding parameter is not provided, the encoding of CSV data is treated as ASCII-8BIT — yu nobuoka <nobuoka@...>

13 messages 2011/04/24

[#35879] [Ruby 1.9 - Bug #4610][Open] Proc#curry behavior is inconsistent with lambdas containing default argument values — Joshua Ballanco <jballanc@...>

11 messages 2011/04/25

[#35883] [Ruby 1.9 - Bug #4611][Open] [BUG] Segementation fault reported — Deryl Doucette <me@...>

15 messages 2011/04/25

[#35895] [Ruby 1.9 - Feature #4614][Open] [RFC/PATCH] thread_pthread.c: lower RUBY_STACK_MIN_LIMIT to 64K — Eric Wong <normalperson@...>

10 messages 2011/04/25

[ruby-core:35669] Re: [Ruby 1.9 - Bug #4558] TestSocket#test_closed_read fails after r31230

From: Eric Wong <normalperson@...>
Date: 2011-04-09 03:07:37 UTC
List: ruby-core #35669
Tomoyuki Chikanaga <redmine@ruby-lang.org> wrote:
> ruby -v changed from - to ruby 1.9.3dev (2011-04-05 trunk 31241) [x86_64-darwin10.6.0]
> 
> Hi,
> 
> I applied Eric's patch, but TestSocket#test_closed_read still report same failure.
> 
> I can reproduce EBADF with following script.

Thanks for testing, think I have a better fix below (supercedes my
original fix)

Also pushed to the "io-close-fixes" branch of git://bogomips.org/ruby.git

From d5f9659ea9c2e8e0ed67544ed35afef4ca2bb3c5 Mon Sep 17 00:00:00 2001
From: Eric Wong <normalperson@yhbt.net>
Date: Thu, 7 Apr 2011 19:25:20 +0000
Subject: [PATCH] io.c (rb_io_close): ensure IOError for cross-thread closes

We need to inform threads to stop operations on the FD before
closing it and also invalidate the fd member of the rb_io_t
struct for other threads to properly raise IOError.

FDs may be created and destroyed without the GVL, so
rb_thread_fd_close() may be improperly hitting the wrong
threads/FDs if we close() before notifying and in the worst case
or threads will end up reading/writing to an unexpected FD.

ref: [ruby-core:35631]
ref: http://redmine.ruby-lang.org/issues/4558
---
 io.c                 |   25 ++++++++++++++++++-------
 test/ruby/test_io.rb |   39 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 57 insertions(+), 7 deletions(-)

diff --git a/io.c b/io.c
index 7ce7148..5d37b7f 100644
--- a/io.c
+++ b/io.c
@@ -3504,6 +3504,7 @@ maygvl_close(int fd, int keepgvl)
     if (keepgvl)
 	return close(fd);
 
+    rb_thread_fd_close(fd);
     /*
      * close() may block for certain file types (NFS, SO_LINGER sockets,
      * inotify), so let other threads run.
@@ -3525,6 +3526,8 @@ maygvl_fclose(FILE *file, int keepgvl)
     if (keepgvl)
 	return fclose(file);
 
+    rb_thread_fd_close(fileno(file));
+
     return (int)rb_thread_blocking_region(nogvl_fclose, file, RUBY_UBF_IO, 0);
 }
 
@@ -3555,24 +3558,35 @@ fptr_finalize(rb_io_t *fptr, int noraise)
 	}
     }
     if (IS_PREP_STDIO(fptr) || fptr->fd <= 2) {
+        int fd = fptr->fd;
+
+        fptr->stdio_file = 0;
+        fptr->fd = -1;
+        rb_thread_fd_close(fd);
         goto skip_fd_close;
     }
     if (fptr->stdio_file) {
+	FILE *stdio_file = fptr->stdio_file;
+
+	fptr->stdio_file = 0;
+	fptr->fd = -1;
+
         /* fptr->stdio_file is deallocated anyway
          * even if fclose failed.  */
-	if ((maygvl_fclose(fptr->stdio_file, noraise) < 0) && NIL_P(err))
+	if ((maygvl_fclose(stdio_file, noraise) < 0) && NIL_P(err))
             err = noraise ? Qtrue : INT2NUM(errno);
     }
     else if (0 <= fptr->fd) {
+	int fd = fptr->fd;
+	fptr->fd = -1;
+
         /* fptr->fd may be closed even if close fails.
          * POSIX doesn't specify it.
          * We assumes it is closed.  */
-	if ((maygvl_close(fptr->fd, noraise) < 0) && NIL_P(err))
+	if ((maygvl_close(fd, noraise) < 0) && NIL_P(err))
 	    err = noraise ? Qtrue : INT2NUM(errno);
     }
   skip_fd_close:
-    fptr->fd = -1;
-    fptr->stdio_file = 0;
     fptr->mode &= ~(FMODE_READABLE|FMODE_WRITABLE);
 
     if (!NIL_P(err) && !noraise) {
@@ -3668,7 +3682,6 @@ VALUE
 rb_io_close(VALUE io)
 {
     rb_io_t *fptr;
-    int fd;
     VALUE write_io;
     rb_io_t *write_fptr;
 
@@ -3684,9 +3697,7 @@ rb_io_close(VALUE io)
     if (!fptr) return Qnil;
     if (fptr->fd < 0) return Qnil;
 
-    fd = fptr->fd;
     rb_io_fptr_cleanup(fptr, FALSE);
-    rb_thread_fd_close(fd);
 
     if (fptr->pid) {
 	rb_syswait(fptr->pid);
diff --git a/test/ruby/test_io.rb b/test/ruby/test_io.rb
index 6b8e6b5..3d086b3 100644
--- a/test/ruby/test_io.rb
+++ b/test/ruby/test_io.rb
@@ -1809,4 +1809,43 @@ End
       Process.waitpid2(pid)
     end
   end
+
+  def test_cross_thread_close_fd
+    with_pipe do |r,w|
+      read_thread = Thread.new do
+        begin
+          r.read(1)
+        rescue => e
+          e
+        end
+      end
+
+      sleep(0.1) until read_thread.stop?
+      r.close
+      read_thread.join
+      assert_kind_of(IOError, read_thread.value)
+    end
+  end
+
+  def test_cross_thread_close_stdio
+    with_pipe do |r,w|
+      pid = fork do
+        $stdin.reopen(r)
+        r.close
+        read_thread = Thread.new do
+          begin
+            $stdin.read(1)
+          rescue => e
+            e
+          end
+        end
+        sleep(0.1) until read_thread.stop?
+        $stdin.close
+        read_thread.join
+        exit(IOError === read_thread.value)
+      end
+      assert Process.waitpid2(pid)[1].success?
+    end
+    rescue NotImplementedError
+  end
 end
-- 
Eric Wong

In This Thread