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]
This commit is contained in:
Jeremy Evans 2020-10-28 15:27:00 -07:00 коммит произвёл GitHub
Родитель a99f52d511
Коммит dfb3605bbe
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
4 изменённых файлов: 68 добавлений и 2 удалений

Просмотреть файл

@ -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

Просмотреть файл

@ -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
@ -3113,6 +3113,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
@ -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;