Thread.report_on_exception
* thread.c (thread_start_func_2): report raised exception if report_on_exception flag is set. [Feature #6647] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55290 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
7451c1468b
commit
2e71c75278
@ -1,3 +1,8 @@
|
|||||||
|
Mon Jun 6 09:25:34 2016 Nobuyoshi Nakada <nobu@ruby-lang.org>
|
||||||
|
|
||||||
|
* 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@rhe.jp>
|
Mon Jun 6 01:36:24 2016 Kazuki Yamaguchi <k@rhe.jp>
|
||||||
|
|
||||||
* ext/openssl/extconf.rb: Check existence of SSL_is_server(). This
|
* ext/openssl/extconf.rb: Check existence of SSL_is_server(). This
|
||||||
|
5
NEWS
5
NEWS
@ -81,6 +81,11 @@ with all sufficient information, see the ChangeLog file or Redmine
|
|||||||
* MatchData#named_captures [Feature #11999]
|
* MatchData#named_captures [Feature #11999]
|
||||||
* MatchData#values_at supports named captures [Feature #9179]
|
* 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)
|
=== Stdlib updates (outstanding ones only)
|
||||||
|
|
||||||
* CSV
|
* CSV
|
||||||
|
@ -85,9 +85,14 @@ set_backtrace(VALUE info, VALUE bt)
|
|||||||
|
|
||||||
static void
|
static void
|
||||||
error_print(rb_thread_t *th)
|
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;
|
volatile VALUE errat = Qundef;
|
||||||
VALUE errinfo = th->errinfo;
|
|
||||||
int raised_flag = th->raised_flag;
|
int raised_flag = th->raised_flag;
|
||||||
volatile VALUE eclass = Qundef, e = Qundef;
|
volatile VALUE eclass = Qundef, e = Qundef;
|
||||||
const char *volatile einfo;
|
const char *volatile einfo;
|
||||||
|
@ -344,6 +344,62 @@ class TestThread < Test::Unit::TestCase
|
|||||||
INPUT
|
INPUT
|
||||||
end
|
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
|
def test_status_and_stop_p
|
||||||
a = ::Thread.new { raise("die now") }
|
a = ::Thread.new { raise("die now") }
|
||||||
b = Thread.new { Thread.stop }
|
b = Thread.new { Thread.stop }
|
||||||
|
123
thread.c
123
thread.c
@ -541,6 +541,7 @@ thread_cleanup_func(void *th_ptr, int atfork)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static VALUE rb_threadptr_raise(rb_thread_t *, int, VALUE *);
|
static VALUE rb_threadptr_raise(rb_thread_t *, int, VALUE *);
|
||||||
|
static VALUE rb_thread_inspect(VALUE thread);
|
||||||
|
|
||||||
void
|
void
|
||||||
ruby_thread_init_stack(rb_thread_t *th)
|
ruby_thread_init_stack(rb_thread_t *th)
|
||||||
@ -609,6 +610,13 @@ thread_start_func_2(rb_thread_t *th, VALUE *stack_start, VALUE *register_stack_s
|
|||||||
th->abort_on_exception || RTEST(ruby_debug)) {
|
th->abort_on_exception || RTEST(ruby_debug)) {
|
||||||
/* exit on main_thread */
|
/* 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 {
|
else {
|
||||||
errinfo = Qnil;
|
errinfo = Qnil;
|
||||||
}
|
}
|
||||||
@ -700,6 +708,7 @@ thread_create_core(VALUE thval, VALUE args, VALUE (*fn)(ANYARGS))
|
|||||||
|
|
||||||
native_mutex_initialize(&th->interrupt_lock);
|
native_mutex_initialize(&th->interrupt_lock);
|
||||||
native_cond_initialize(&th->interrupt_cond, RB_CONDATTR_CLOCK_MONOTONIC);
|
native_cond_initialize(&th->interrupt_cond, RB_CONDATTR_CLOCK_MONOTONIC);
|
||||||
|
th->report_on_exception = th->vm->thread_report_on_exception;
|
||||||
|
|
||||||
/* kick thread */
|
/* kick thread */
|
||||||
err = native_thread_create(th);
|
err = native_thread_create(th);
|
||||||
@ -2593,6 +2602,116 @@ rb_thread_abort_exc_set(VALUE thread, VALUE val)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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:
|
* call-seq:
|
||||||
* thr.group -> thgrp or nil
|
* thr.group -> thgrp or nil
|
||||||
@ -4633,6 +4752,8 @@ Init_Thread(void)
|
|||||||
rb_define_singleton_method(rb_cThread, "list", rb_thread_list, 0);
|
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, 0);
|
||||||
rb_define_singleton_method(rb_cThread, "abort_on_exception=", rb_thread_s_abort_exc_set, 1);
|
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
|
#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, 0);
|
||||||
rb_define_singleton_method(rb_cThread, "DEBUG=", rb_thread_s_debug_set, 1);
|
rb_define_singleton_method(rb_cThread, "DEBUG=", rb_thread_s_debug_set, 1);
|
||||||
@ -4665,6 +4786,8 @@ Init_Thread(void)
|
|||||||
rb_define_method(rb_cThread, "stop?", rb_thread_stop_p, 0);
|
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, 0);
|
||||||
rb_define_method(rb_cThread, "abort_on_exception=", rb_thread_abort_exc_set, 1);
|
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, "safe_level", rb_thread_safe_level, 0);
|
||||||
rb_define_method(rb_cThread, "group", rb_thread_group, 0);
|
rb_define_method(rb_cThread, "group", rb_thread_group, 0);
|
||||||
rb_define_method(rb_cThread, "backtrace", rb_thread_backtrace_m, -1);
|
rb_define_method(rb_cThread, "backtrace", rb_thread_backtrace_m, -1);
|
||||||
|
@ -495,6 +495,7 @@ typedef struct rb_vm_struct {
|
|||||||
|
|
||||||
unsigned int running: 1;
|
unsigned int running: 1;
|
||||||
unsigned int thread_abort_on_exception: 1;
|
unsigned int thread_abort_on_exception: 1;
|
||||||
|
unsigned int thread_report_on_exception: 1;
|
||||||
unsigned int trace_running: 1;
|
unsigned int trace_running: 1;
|
||||||
volatile int sleeper;
|
volatile int sleeper;
|
||||||
|
|
||||||
@ -786,6 +787,7 @@ typedef struct rb_thread_struct {
|
|||||||
/* misc */
|
/* misc */
|
||||||
enum method_missing_reason method_missing_reason: 8;
|
enum method_missing_reason method_missing_reason: 8;
|
||||||
unsigned int abort_on_exception: 1;
|
unsigned int abort_on_exception: 1;
|
||||||
|
unsigned int report_on_exception: 1;
|
||||||
#ifdef USE_SIGALTSTACK
|
#ifdef USE_SIGALTSTACK
|
||||||
void *altstack;
|
void *altstack;
|
||||||
#endif
|
#endif
|
||||||
@ -1149,6 +1151,7 @@ void rb_threadptr_unlock_all_locking_mutexes(rb_thread_t *th);
|
|||||||
void rb_threadptr_pending_interrupt_clear(rb_thread_t *th);
|
void rb_threadptr_pending_interrupt_clear(rb_thread_t *th);
|
||||||
void rb_threadptr_pending_interrupt_enque(rb_thread_t *th, VALUE v);
|
void rb_threadptr_pending_interrupt_enque(rb_thread_t *th, VALUE v);
|
||||||
int rb_threadptr_pending_interrupt_active_p(rb_thread_t *th);
|
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)
|
#define RUBY_VM_CHECK_INTS(th) ruby_vm_check_ints(th)
|
||||||
static inline void
|
static inline void
|
||||||
|
Loading…
x
Reference in New Issue
Block a user