summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog13
-rw-r--r--ext/-test-/gvl/call_without_gvl/call_without_gvl.c35
-rw-r--r--ext/-test-/gvl/call_without_gvl/extconf.rb1
-rw-r--r--test/-ext-/gvl/test_last_thread.rb22
-rw-r--r--thread.c2
5 files changed, 72 insertions, 1 deletions
diff --git a/ChangeLog b/ChangeLog
index e8b3838ddb6..9f786630fea 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+Sat Jun 13 07:21:18 2015 KOSAKI Motohiro <kosaki.motohiro@gmail.com>
+
+ * thread.c (thread_start_func_2): don't interrupt when last thread
+ exit unless main thread is already exited. Otherwise main thread
+ could be wrongly interrupted when it uses rb_thread_call_without_gvl().
+ Patch by Takehiro Kubo. [Bug #11237][ruby-dev:49044][GH-898]
+
+ * test/-ext-/gvl/test_last_thread.rb: new test for the above fix.
+
+ * ext/-test-/gvl/call_without_gvl/call_without_gvl.c: new ext for
+ the above test.
+ * ext/-test-/gvl/call_without_gvl/extconf.rb: ditto.
+
Mon Jun 15 00:14:33 2015 Tanaka Akira <akr@fsij.org>
* ext/pathname/lib/pathname.rb (descend): Blockless form supported.
diff --git a/ext/-test-/gvl/call_without_gvl/call_without_gvl.c b/ext/-test-/gvl/call_without_gvl/call_without_gvl.c
new file mode 100644
index 00000000000..0ee64a24ac9
--- /dev/null
+++ b/ext/-test-/gvl/call_without_gvl/call_without_gvl.c
@@ -0,0 +1,35 @@
+#include "ruby/ruby.h"
+#include "ruby/thread.h"
+#include <sys/select.h>
+
+static void*
+native_sleep_callback(void *data)
+{
+ struct timeval *timeval = data;
+ select(0, NULL, NULL, NULL, timeval);
+
+ return NULL;
+}
+
+
+static VALUE
+thread_runnable_sleep(VALUE thread, VALUE timeout)
+{
+ struct timeval timeval;
+
+ if (NIL_P(timeout)) {
+ rb_raise(rb_eArgError, "timeout must be non nil");
+ }
+
+ timeval = rb_time_interval(timeout);
+
+ rb_thread_call_without_gvl(native_sleep_callback, &timeval, RUBY_UBF_IO, NULL);
+
+ return thread;
+}
+
+void
+Init_call_without_gvl(void)
+{
+ rb_define_method(rb_cThread, "__runnable_sleep__", thread_runnable_sleep, 1);
+}
diff --git a/ext/-test-/gvl/call_without_gvl/extconf.rb b/ext/-test-/gvl/call_without_gvl/extconf.rb
new file mode 100644
index 00000000000..601944c9e56
--- /dev/null
+++ b/ext/-test-/gvl/call_without_gvl/extconf.rb
@@ -0,0 +1 @@
+create_makefile("-test-/gvl/call_without_gvl")
diff --git a/test/-ext-/gvl/test_last_thread.rb b/test/-ext-/gvl/test_last_thread.rb
new file mode 100644
index 00000000000..df25165b4b5
--- /dev/null
+++ b/test/-ext-/gvl/test_last_thread.rb
@@ -0,0 +1,22 @@
+class TestLastThread < Test::Unit::TestCase
+
+ # [Bug #11237]
+ def test_last_thread
+
+ assert_separately([], <<-"end;") #do
+ require '-test-/gvl/call_without_gvl'
+
+ Thread.new {
+ sleep 0.2
+ }
+
+ t0 = Time.now
+ Thread.current.__runnable_sleep__ 1
+ t1 = Time.now
+ t = t1 - t0
+
+ assert_operator(t, :>=, 1)
+ end;
+ end
+end
+
diff --git a/thread.c b/thread.c
index bf045a44ea3..8e8d8fbdfe4 100644
--- a/thread.c
+++ b/thread.c
@@ -629,7 +629,7 @@ thread_start_func_2(rb_thread_t *th, VALUE *stack_start, VALUE *register_stack_s
/* delete self other than main thread from living_threads */
rb_vm_living_threads_remove(th->vm, th);
- if (rb_thread_alone()) {
+ if (main_th->status == THREAD_KILLED && rb_thread_alone()) {
/* I'm last thread. wake up main thread from rb_thread_terminate_all */
rb_threadptr_interrupt(main_th);
}