ruby-changes:43216
From: nobu <ko1@a...>
Date: Mon, 6 Jun 2016 09:25:46 +0900 (JST)
Subject: [ruby-changes:43216] nobu:r55290 (trunk): Thread.report_on_exception
nobu 2016-06-06 09:25:38 +0900 (Mon, 06 Jun 2016) New Revision: 55290 https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=55290 Log: Thread.report_on_exception * thread.c (thread_start_func_2): report raised exception if report_on_exception flag is set. [Feature #6647] Modified files: trunk/ChangeLog trunk/NEWS trunk/eval_error.c trunk/test/ruby/test_thread.rb trunk/thread.c trunk/vm_core.h Index: eval_error.c =================================================================== --- eval_error.c (revision 55289) +++ eval_error.c (revision 55290) @@ -86,8 +86,13 @@ set_backtrace(VALUE info, VALUE bt) https://github.com/ruby/ruby/blob/trunk/eval_error.c#L86 static void error_print(rb_thread_t *th) { + rb_threadptr_error_print(th, th->errinfo); +} + +void +rb_threadptr_error_print(rb_thread_t *th, VALUE errinfo) +{ volatile VALUE errat = Qundef; - VALUE errinfo = th->errinfo; int raised_flag = th->raised_flag; volatile VALUE eclass = Qundef, e = Qundef; const char *volatile einfo; Index: NEWS =================================================================== --- NEWS (revision 55289) +++ NEWS (revision 55290) @@ -81,6 +81,11 @@ with all sufficient information, see the https://github.com/ruby/ruby/blob/trunk/NEWS#L81 * MatchData#named_captures [Feature #11999] * MatchData#values_at supports named captures [Feature #9179] +* Thread + + * Thread#report_on_exception and Thread.report_on_exception + [Feature #6647] + === Stdlib updates (outstanding ones only) * CSV Index: ChangeLog =================================================================== --- ChangeLog (revision 55289) +++ ChangeLog (revision 55290) @@ -1,3 +1,8 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1 +Mon Jun 6 09:25:34 2016 Nobuyoshi Nakada <nobu@r...> + + * thread.c (thread_start_func_2): report raised exception if + report_on_exception flag is set. [Feature #6647] + Mon Jun 6 01:36:24 2016 Kazuki Yamaguchi <k@r...> * ext/openssl/extconf.rb: Check existence of SSL_is_server(). This Index: test/ruby/test_thread.rb =================================================================== --- test/ruby/test_thread.rb (revision 55289) +++ test/ruby/test_thread.rb (revision 55290) @@ -344,6 +344,62 @@ class TestThread < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_thread.rb#L344 INPUT end + def test_report_on_exception + assert_separately([], <<~"end;") #do + q1 = Queue.new + q2 = Queue.new + + assert_equal(false, Thread.report_on_exception, + "global flags is false by default") + assert_equal(false, Thread.current.report_on_exception) + + Thread.current.report_on_exception = true + assert_equal(false, + Thread.start {Thread.current.report_on_exception}.value, + "should not inherit from the parent thread") + + assert_warn("", "exception should be ignored silently") { + th = Thread.start { + q1.push(Thread.current.report_on_exception) + raise "report 1" + } + assert_equal(false, q1.pop) + Thread.pass while th.alive? + } + + assert_warn(/report 2/, "exception should be reported") { + th = Thread.start { + q1.push(Thread.current.report_on_exception = true) + raise "report 2" + } + assert_equal(true, q1.pop) + Thread.pass while th.alive? + } + + assert_equal(false, Thread.report_on_exception) + assert_warn("", "the global flag should not affect already started threads") { + th = Thread.start { + q2.pop + q1.push(Thread.current.report_on_exception) + raise "report 3" + } + q2.push(Thread.report_on_exception = true) + assert_equal(false, q1.pop) + Thread.pass while th.alive? + } + + assert_equal(true, Thread.report_on_exception) + assert_warn(/report 4/, "should defaults to the global flag at the start") { + th = Thread.start { + q1.push(Thread.current.report_on_exception) + raise "report 4" + } + assert_equal(true, q1.pop) + Thread.pass while th.alive? + } + end; + end + def test_status_and_stop_p a = ::Thread.new { raise("die now") } b = Thread.new { Thread.stop } Index: vm_core.h =================================================================== --- vm_core.h (revision 55289) +++ vm_core.h (revision 55290) @@ -495,6 +495,7 @@ typedef struct rb_vm_struct { https://github.com/ruby/ruby/blob/trunk/vm_core.h#L495 unsigned int running: 1; unsigned int thread_abort_on_exception: 1; + unsigned int thread_report_on_exception: 1; unsigned int trace_running: 1; volatile int sleeper; @@ -786,6 +787,7 @@ typedef struct rb_thread_struct { https://github.com/ruby/ruby/blob/trunk/vm_core.h#L787 /* misc */ enum method_missing_reason method_missing_reason: 8; unsigned int abort_on_exception: 1; + unsigned int report_on_exception: 1; #ifdef USE_SIGALTSTACK void *altstack; #endif @@ -1149,6 +1151,7 @@ void rb_threadptr_unlock_all_locking_mut https://github.com/ruby/ruby/blob/trunk/vm_core.h#L1151 void rb_threadptr_pending_interrupt_clear(rb_thread_t *th); void rb_threadptr_pending_interrupt_enque(rb_thread_t *th, VALUE v); int rb_threadptr_pending_interrupt_active_p(rb_thread_t *th); +void rb_threadptr_error_print(rb_thread_t *th, VALUE errinfo); #define RUBY_VM_CHECK_INTS(th) ruby_vm_check_ints(th) static inline void Index: thread.c =================================================================== --- thread.c (revision 55289) +++ thread.c (revision 55290) @@ -541,6 +541,7 @@ thread_cleanup_func(void *th_ptr, int at https://github.com/ruby/ruby/blob/trunk/thread.c#L541 } static VALUE rb_threadptr_raise(rb_thread_t *, int, VALUE *); +static VALUE rb_thread_inspect(VALUE thread); void ruby_thread_init_stack(rb_thread_t *th) @@ -609,6 +610,13 @@ thread_start_func_2(rb_thread_t *th, VAL https://github.com/ruby/ruby/blob/trunk/thread.c#L610 th->abort_on_exception || RTEST(ruby_debug)) { /* exit on main_thread */ } + else if (th->report_on_exception) { + VALUE mesg = rb_thread_inspect(th->self); + rb_str_cat_cstr(mesg, " terminated with exception:\n"); + rb_write_error_str(mesg); + rb_threadptr_error_print(th, errinfo); + errinfo = Qnil; + } else { errinfo = Qnil; } @@ -700,6 +708,7 @@ thread_create_core(VALUE thval, VALUE ar https://github.com/ruby/ruby/blob/trunk/thread.c#L708 native_mutex_initialize(&th->interrupt_lock); native_cond_initialize(&th->interrupt_cond, RB_CONDATTR_CLOCK_MONOTONIC); + th->report_on_exception = th->vm->thread_report_on_exception; /* kick thread */ err = native_thread_create(th); @@ -2595,6 +2604,116 @@ rb_thread_abort_exc_set(VALUE thread, VA https://github.com/ruby/ruby/blob/trunk/thread.c#L2604 /* * call-seq: + * Thread.report_on_exception -> true or false + * + * Returns the status of the global ``report on exception'' condition. + * + * The default is +false+. + * + * When set to +true+, all threads will report the exception if an + * exception is raised in any thread. + * + * See also ::report_on_exception=. + * + * There is also an instance level method to set this for a specific thread, + * see #report_on_exception. + */ + +static VALUE +rb_thread_s_report_exc(void) +{ + return GET_THREAD()->vm->thread_report_on_exception ? Qtrue : Qfalse; +} + + +/* + * call-seq: + * Thread.report_on_exception= boolean -> true or false + * + * When set to +true+, all threads will report the exception if an + * exception is raised. Returns the new state. + * + * Thread.report_on_exception = true + * t1 = Thread.new do + * puts "In new thread" + * raise "Exception from thread" + * end + * sleep(1) + * puts "In the main thread" + * + * This will produce: + * + * In new thread + * prog.rb:4: Exception from thread (RuntimeError) + * from prog.rb:2:in `initialize' + * from prog.rb:2:in `new' + * from prog.rb:2 + * In the main thread + * + * See also ::report_on_exception. + * + * There is also an instance level method to set this for a specific thread, + * see #report_on_exception=. + */ + +static VALUE +rb_thread_s_report_exc_set(VALUE self, VALUE val) +{ + GET_THREAD()->vm->thread_report_on_exception = 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 + * this +thr+. + * + * The default is +false+. + * + * See also #report_on_exception=. + * + * There is also a class level method to set this for all threads, see + * ::report_on_exception. + */ + +static VALUE +rb_thread_report_exc(VALUE thread) +{ + rb_thread_t *th; + GetThreadPtr(thread, th); + return th->report_on_exception ? Qtrue : Qfalse; +} + + +/* + * call-seq: + * thr.report_on_exception= boolean -> true or false + * + * When set to +true+, all threads (including the main program) will + * report the exception if an exception is raised in this +thr+. + * + * See also #report_on_exception. + * + * There is also a class level method to set this for all threads, see + * ::report_on_exception=. + */ + +static VALUE +rb_thread_report_exc_set(VALUE thread, VALUE val) +{ + rb_thread_t *th; + + GetThreadPtr(thread, th); + th->report_on_exception = RTEST(val); + return val; +} + + +/* + * call-seq: * thr.group -> thgrp or nil * * Returns the ThreadGroup which contains the given thread, or returns +nil+ @@ -4633,6 +4752,8 @@ Init_Thread(void) https://github.com/ruby/ruby/blob/trunk/thread.c#L4752 rb_define_singleton_method(rb_cThread, "list", rb_thread_list, 0); rb_define_singleton_method(rb_cThread, "abort_on_exception", rb_thread_s_abort_exc, 0); 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); #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); @@ -4665,6 +4786,8 @@ Init_Thread(void) https://github.com/ruby/ruby/blob/trunk/thread.c#L4786 rb_define_method(rb_cThread, "stop?", rb_thread_stop_p, 0); rb_define_method(rb_cThread, "abort_on_exception", rb_thread_abort_exc, 0); rb_define_method(rb_cThread, "abort_on_exception=", rb_thread_abort_exc_set, 1); + rb_define_method(rb_cThread, "report_on_exception", rb_thread_report_exc, 0); + rb_define_method(rb_cThread, "report_on_exception=", rb_thread_report_exc_set, 1); rb_define_method(rb_cThread, "safe_level", rb_thread_safe_level, 0); rb_define_method(rb_cThread, "group", rb_thread_group, 0); rb_define_method(rb_cThread, "backtrace", rb_thread_backtrace_m, -1); -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/