[前][次][番号順一覧][スレッド一覧]

ruby-changes:63467

From: Jeremy <ko1@a...>
Date: Thu, 29 Oct 2020 07:27:21 +0900 (JST)
Subject: [ruby-changes:63467] dfb3605bbe (master): Add Thread.ignore_deadlock accessor

https://git.ruby-lang.org/ruby.git/commit/?id=dfb3605bbe

From dfb3605bbee9c3cfbc1c354594c367472f29cb35 Mon Sep 17 00:00:00 2001
From: Jeremy Evans <code@j...>
Date: Wed, 28 Oct 2020 15:27:00 -0700
Subject: Add Thread.ignore_deadlock accessor

Setting this to true disables the deadlock detector.  It should
only be used in cases where the deadlock could be broken via some
external means, such as via a signal.

Now that $SAFE is no longer used, replace the safe_level_ VM flag
with ignore_deadlock for storing the setting.

Fixes [Bug #13768]

diff --git a/NEWS.md b/NEWS.md
index 83b7a5a..c7a4036 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -178,6 +178,9 @@ Outstanding ones only. https://github.com/ruby/ruby/blob/trunk/NEWS.md#L178
       blocking. [[Feature #16786]]
     * `Thread#join` invokes the scheduler hooks `block`/`unblock` in a
       non-blocking execution context. [[Feature #16786]]
+    * `Thread.ignore_deadlock` accessor for disabling the default deadlock
+      detection, allowing the use of signal handlers to break deadlock.
+      [[Bug #13768]]
 
 * Mutex
 
diff --git a/test/ruby/test_thread.rb b/test/ruby/test_thread.rb
index 30a3cc7..0af7a37 100644
--- a/test/ruby/test_thread.rb
+++ b/test/ruby/test_thread.rb
@@ -490,6 +490,19 @@ class TestThread < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_thread.rb#L490
     end;
   end
 
+  def test_ignore_deadlock
+    if /mswin|mingw/ =~ RUBY_PLATFORM
+      skip "can't trap a signal from another process on Windows"
+    end
+    assert_in_out_err([], <<-INPUT, %w(false :sig), [], :signal=>:INT, timeout: 1, timeout_error: nil)
+      p Thread.ignore_deadlock
+      q = Queue.new
+      trap(:INT){q.push :sig}
+      Thread.ignore_deadlock = true
+      p q.pop
+    INPUT
+  end
+
   def test_status_and_stop_p
     a = ::Thread.new {
       Thread.current.report_on_exception = false
diff --git a/thread.c b/thread.c
index 64d9ac0..007a5cb 100644
--- a/thread.c
+++ b/thread.c
@@ -3065,7 +3065,7 @@ rb_thread_abort_exc_set(VALUE thread, VALUE val) https://github.com/ruby/ruby/blob/trunk/thread.c#L3065
  *
  *  There is also an instance level method to set this for a specific thread,
  *  see #report_on_exception=.
-  *
+ *
  */
 
 static VALUE
@@ -3115,6 +3115,52 @@ rb_thread_s_report_exc_set(VALUE self, VALUE val) https://github.com/ruby/ruby/blob/trunk/thread.c#L3115
 
 /*
  *  call-seq:
+ *     Thread.ignore_deadlock -> true or false
+ *
+ *  Returns the status of the global ``ignore deadlock'' condition.
+ *  The default is +false+, so that deadlock conditions are not ignored.
+ *
+ *  See also ::ignore_deadlock=.
+ *
+ */
+
+static VALUE
+rb_thread_s_ignore_deadlock(VALUE _)
+{
+    return GET_THREAD()->vm->thread_ignore_deadlock ? Qtrue : Qfalse;
+}
+
+
+/*
+ *  call-seq:
+ *     Thread.ignore_deadlock = boolean   -> true or false
+ *
+ *  Returns the new state.
+ *  When set to +true+, the VM will not check for deadlock conditions.
+ *  It is only useful to set this if your application can break a
+ *  deadlock condition via some other means, such as a signal.
+ *
+ *     Thread.ignore_deadlock = true
+ *     queue = Queue.new
+ *
+ *     trap(:SIGUSR1){queue.push "Received signal"}
+ *
+ *     # raises fatal error unless ignoring deadlock
+ *     puts queue.pop
+ *
+ *  See also ::ignore_deadlock.
+ */
+
+static VALUE
+rb_thread_s_ignore_deadlock_set(VALUE self, VALUE val)
+{
+    GET_THREAD()->vm->thread_ignore_deadlock = RTEST(val);
+    return val;
+}
+
+
+/*
+ *  call-seq:
  *     thr.report_on_exception   -> true or false
  *
  *  Returns the status of the thread-local ``report on exception'' condition for
@@ -5480,6 +5526,8 @@ Init_Thread(void) https://github.com/ruby/ruby/blob/trunk/thread.c#L5526
     rb_define_singleton_method(rb_cThread, "abort_on_exception=", rb_thread_s_abort_exc_set, 1);
     rb_define_singleton_method(rb_cThread, "report_on_exception", rb_thread_s_report_exc, 0);
     rb_define_singleton_method(rb_cThread, "report_on_exception=", rb_thread_s_report_exc_set, 1);
+    rb_define_singleton_method(rb_cThread, "ignore_deadlock", rb_thread_s_ignore_deadlock, 0);
+    rb_define_singleton_method(rb_cThread, "ignore_deadlock=", rb_thread_s_ignore_deadlock_set, 1);
 #if THREAD_DEBUG < 0
     rb_define_singleton_method(rb_cThread, "DEBUG", rb_thread_s_debug, 0);
     rb_define_singleton_method(rb_cThread, "DEBUG=", rb_thread_s_debug_set, 1);
@@ -5611,6 +5659,8 @@ debug_deadlock_check(rb_ractor_t *r, VALUE msg) https://github.com/ruby/ruby/blob/trunk/thread.c#L5659
 static void
 rb_check_deadlock(rb_ractor_t *r)
 {
+    if (GET_THREAD()->vm->thread_ignore_deadlock) return;
+
     int found = 0;
     rb_thread_t *th = NULL;
     int sleeper_num = rb_ractor_sleeper_thread_num(r);
diff --git a/vm_core.h b/vm_core.h
index 8525bfc..02e777f 100644
--- a/vm_core.h
+++ b/vm_core.h
@@ -595,7 +595,7 @@ typedef struct rb_vm_struct { https://github.com/ruby/ruby/blob/trunk/vm_core.h#L595
     unsigned int running: 1;
     unsigned int thread_abort_on_exception: 1;
     unsigned int thread_report_on_exception: 1;
-    unsigned int safe_level_: 1;
+    unsigned int thread_ignore_deadlock: 1;
 
     /* object management */
     VALUE mark_object_ary;
-- 
cgit v0.10.2


--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]