summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS.md3
-rw-r--r--test/ruby/test_thread.rb13
-rw-r--r--thread.c52
-rw-r--r--vm_core.h2
4 files changed, 68 insertions, 2 deletions
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.
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
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)
*
* 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)
/*
* 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)
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)
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 {
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;