diff options
-rw-r--r-- | NEWS.md | 3 | ||||
-rw-r--r-- | test/ruby/test_thread.rb | 13 | ||||
-rw-r--r-- | thread.c | 52 | ||||
-rw-r--r-- | vm_core.h | 2 |
4 files changed, 68 insertions, 2 deletions
@@ -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 30a3cc784e..0af7a37cf8 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 @@ -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); @@ -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; |