about summary refs log tree commit homepage
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2011-03-10 04:35:06 +0000
committerEric Wong <normalperson@yhbt.net>2011-03-10 04:35:06 +0000
commit3721229d20880694f3c5a3a7a3fa401b19c5870b (patch)
treefd22b9e7c5b536c67efbd8a3ce74f583e5f29e58
parent5905791c3373cf9cd1fce4da28acf9b16f721c3a (diff)
downloadsleepy_penguin-3721229d20880694f3c5a3a7a3fa401b19c5870b.tar.gz
close(2) on inotify descriptors takes forever and a day.
-rw-r--r--ext/sleepy_penguin/inotify.c40
-rw-r--r--test/test_inotify.rb23
2 files changed, 62 insertions, 1 deletions
diff --git a/ext/sleepy_penguin/inotify.c b/ext/sleepy_penguin/inotify.c
index a08bf15..6c126b4 100644
--- a/ext/sleepy_penguin/inotify.c
+++ b/ext/sleepy_penguin/inotify.c
@@ -3,6 +3,11 @@
 #include <sys/inotify.h>
 #include <sys/ioctl.h>
 #include "missing_inotify.h"
+#if defined(RFILE) && defined(HAVE_ST_FD) && \
+    defined(HAVE_RB_THREAD_BLOCKING_REGION)
+#  define NOGVL_CLOSE
+#endif
+
 static ID id_for_fd, id_inotify_buf, id_inotify_tmp, id_mask;
 static VALUE cEvent, checks;
 
@@ -265,7 +270,7 @@ static VALUE init_copy(VALUE dest, VALUE orig)
 
 /*
  * call-seq:
- *         ino.each { |event| ... } -> ino
+ *        ino.each { |event| ... } -> ino
  *
  * Yields each Inotify::Event received in a blocking fashion.
  */
@@ -279,6 +284,36 @@ static VALUE each(VALUE self)
         return self;
 }
 
+#if defined(NOGVL_CLOSE)
+static VALUE fptr_close(void *ptr)
+{
+        rb_io_t *fptr = ptr;
+        return (VALUE)close(fptr->fd);
+}
+
+/*
+ * call-seq:
+ *        ino.close        -> nil
+ *
+ * Closes the underlying file descriptor and releases resources used by the
+ * kernel.  Unlike other file descriptors, Inotify descriptors can take
+ * a long time to close(2).  Calling this explicitly releases the GVL under
+ * Ruby 1.9
+ */
+static VALUE nogvl_close(VALUE self)
+{
+        rb_io_t *fptr;
+
+        GetOpenFile(self, fptr);
+
+        if ((int)rb_sp_io_region(fptr_close, fptr) < 0)
+                rb_sys_fail("close(inotify)");
+        fptr->fd = -1;
+
+        return Qnil;
+}
+#endif /* NOGVL_CLOSE */
+
 void sleepy_penguin_init_inotify(void)
 {
         VALUE mSleepyPenguin, cInotify;
@@ -311,6 +346,9 @@ void sleepy_penguin_init_inotify(void)
         rb_define_method(cInotify, "initialize_copy", init_copy, 1);
         rb_define_method(cInotify, "take", take, -1);
         rb_define_method(cInotify, "each", each, 0);
+#ifdef NOGVL_CLOSE
+        rb_define_method(cInotify, "close", nogvl_close, 0);
+#endif
 
         /*
          * Document-class: SleepyPenguin::Inotify::Event
diff --git a/test/test_inotify.rb b/test/test_inotify.rb
index a003baf..1a5d5d1 100644
--- a/test/test_inotify.rb
+++ b/test/test_inotify.rb
@@ -115,4 +115,27 @@ class TestInotify < Test::Unit::TestCase
     end
     assert_equal 0, nr
   end
+
+  def test_close_threadable
+    ino = Inotify.new
+    tmp = []
+    thr = Thread.new do
+      until ino.closed?
+        tmp << Time.now
+        Thread.pass
+      end
+    end
+    t0 = Time.now
+    ino.close
+    t1 = Time.now
+    between = []
+    thr.join
+    tmp.each do |t|
+      if t > t0 && t < t1
+        between << t
+      end
+    end
+    assert tmp.size > 0, "tmp.size=#{tmp.size}"
+    assert between.size > 0, "between.size=#{between.size}"
+  end if RUBY_VERSION.to_f >= 1.9
 end