Skip to content

Commit 2e71c75

Browse files
committed
Thread.report_on_exception
* thread.c (thread_start_func_2): report raised exception if report_on_exception flag is set. [Feature ruby#6647] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55290 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
1 parent 7451c14 commit 2e71c75

File tree

6 files changed

+198
-1
lines changed

6 files changed

+198
-1
lines changed

ChangeLog

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
Mon Jun 6 09:25:34 2016 Nobuyoshi Nakada <nobu@ruby-lang.org>
2+
3+
* thread.c (thread_start_func_2): report raised exception if
4+
report_on_exception flag is set. [Feature #6647]
5+
16
Mon Jun 6 01:36:24 2016 Kazuki Yamaguchi <k@rhe.jp>
27

38
* ext/openssl/extconf.rb: Check existence of SSL_is_server(). This

NEWS

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,11 @@ with all sufficient information, see the ChangeLog file or Redmine
8181
* MatchData#named_captures [Feature #11999]
8282
* MatchData#values_at supports named captures [Feature #9179]
8383

84+
* Thread
85+
86+
* Thread#report_on_exception and Thread.report_on_exception
87+
[Feature #6647]
88+
8489
=== Stdlib updates (outstanding ones only)
8590

8691
* CSV

eval_error.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,14 @@ set_backtrace(VALUE info, VALUE bt)
8585

8686
static void
8787
error_print(rb_thread_t *th)
88+
{
89+
rb_threadptr_error_print(th, th->errinfo);
90+
}
91+
92+
void
93+
rb_threadptr_error_print(rb_thread_t *th, VALUE errinfo)
8894
{
8995
volatile VALUE errat = Qundef;
90-
VALUE errinfo = th->errinfo;
9196
int raised_flag = th->raised_flag;
9297
volatile VALUE eclass = Qundef, e = Qundef;
9398
const char *volatile einfo;

test/ruby/test_thread.rb

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,62 @@ def test_abort_on_exception
344344
INPUT
345345
end
346346

347+
def test_report_on_exception
348+
assert_separately([], <<~"end;") #do
349+
q1 = Queue.new
350+
q2 = Queue.new
351+
352+
assert_equal(false, Thread.report_on_exception,
353+
"global flags is false by default")
354+
assert_equal(false, Thread.current.report_on_exception)
355+
356+
Thread.current.report_on_exception = true
357+
assert_equal(false,
358+
Thread.start {Thread.current.report_on_exception}.value,
359+
"should not inherit from the parent thread")
360+
361+
assert_warn("", "exception should be ignored silently") {
362+
th = Thread.start {
363+
q1.push(Thread.current.report_on_exception)
364+
raise "report 1"
365+
}
366+
assert_equal(false, q1.pop)
367+
Thread.pass while th.alive?
368+
}
369+
370+
assert_warn(/report 2/, "exception should be reported") {
371+
th = Thread.start {
372+
q1.push(Thread.current.report_on_exception = true)
373+
raise "report 2"
374+
}
375+
assert_equal(true, q1.pop)
376+
Thread.pass while th.alive?
377+
}
378+
379+
assert_equal(false, Thread.report_on_exception)
380+
assert_warn("", "the global flag should not affect already started threads") {
381+
th = Thread.start {
382+
q2.pop
383+
q1.push(Thread.current.report_on_exception)
384+
raise "report 3"
385+
}
386+
q2.push(Thread.report_on_exception = true)
387+
assert_equal(false, q1.pop)
388+
Thread.pass while th.alive?
389+
}
390+
391+
assert_equal(true, Thread.report_on_exception)
392+
assert_warn(/report 4/, "should defaults to the global flag at the start") {
393+
th = Thread.start {
394+
q1.push(Thread.current.report_on_exception)
395+
raise "report 4"
396+
}
397+
assert_equal(true, q1.pop)
398+
Thread.pass while th.alive?
399+
}
400+
end;
401+
end
402+
347403
def test_status_and_stop_p
348404
a = ::Thread.new { raise("die now") }
349405
b = Thread.new { Thread.stop }

thread.c

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,7 @@ thread_cleanup_func(void *th_ptr, int atfork)
541541
}
542542

543543
static VALUE rb_threadptr_raise(rb_thread_t *, int, VALUE *);
544+
static VALUE rb_thread_inspect(VALUE thread);
544545

545546
void
546547
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
609610
th->abort_on_exception || RTEST(ruby_debug)) {
610611
/* exit on main_thread */
611612
}
613+
else if (th->report_on_exception) {
614+
VALUE mesg = rb_thread_inspect(th->self);
615+
rb_str_cat_cstr(mesg, " terminated with exception:\n");
616+
rb_write_error_str(mesg);
617+
rb_threadptr_error_print(th, errinfo);
618+
errinfo = Qnil;
619+
}
612620
else {
613621
errinfo = Qnil;
614622
}
@@ -700,6 +708,7 @@ thread_create_core(VALUE thval, VALUE args, VALUE (*fn)(ANYARGS))
700708

701709
native_mutex_initialize(&th->interrupt_lock);
702710
native_cond_initialize(&th->interrupt_cond, RB_CONDATTR_CLOCK_MONOTONIC);
711+
th->report_on_exception = th->vm->thread_report_on_exception;
703712

704713
/* kick thread */
705714
err = native_thread_create(th);
@@ -2593,6 +2602,116 @@ rb_thread_abort_exc_set(VALUE thread, VALUE val)
25932602
}
25942603

25952604

2605+
/*
2606+
* call-seq:
2607+
* Thread.report_on_exception -> true or false
2608+
*
2609+
* Returns the status of the global ``report on exception'' condition.
2610+
*
2611+
* The default is +false+.
2612+
*
2613+
* When set to +true+, all threads will report the exception if an
2614+
* exception is raised in any thread.
2615+
*
2616+
* See also ::report_on_exception=.
2617+
*
2618+
* There is also an instance level method to set this for a specific thread,
2619+
* see #report_on_exception.
2620+
*/
2621+
2622+
static VALUE
2623+
rb_thread_s_report_exc(void)
2624+
{
2625+
return GET_THREAD()->vm->thread_report_on_exception ? Qtrue : Qfalse;
2626+
}
2627+
2628+
2629+
/*
2630+
* call-seq:
2631+
* Thread.report_on_exception= boolean -> true or false
2632+
*
2633+
* When set to +true+, all threads will report the exception if an
2634+
* exception is raised. Returns the new state.
2635+
*
2636+
* Thread.report_on_exception = true
2637+
* t1 = Thread.new do
2638+
* puts "In new thread"
2639+
* raise "Exception from thread"
2640+
* end
2641+
* sleep(1)
2642+
* puts "In the main thread"
2643+
*
2644+
* This will produce:
2645+
*
2646+
* In new thread
2647+
* prog.rb:4: Exception from thread (RuntimeError)
2648+
* from prog.rb:2:in `initialize'
2649+
* from prog.rb:2:in `new'
2650+
* from prog.rb:2
2651+
* In the main thread
2652+
*
2653+
* See also ::report_on_exception.
2654+
*
2655+
* There is also an instance level method to set this for a specific thread,
2656+
* see #report_on_exception=.
2657+
*/
2658+
2659+
static VALUE
2660+
rb_thread_s_report_exc_set(VALUE self, VALUE val)
2661+
{
2662+
GET_THREAD()->vm->thread_report_on_exception = RTEST(val);
2663+
return val;
2664+
}
2665+
2666+
2667+
/*
2668+
* call-seq:
2669+
* thr.report_on_exception -> true or false
2670+
*
2671+
* Returns the status of the thread-local ``report on exception'' condition for
2672+
* this +thr+.
2673+
*
2674+
* The default is +false+.
2675+
*
2676+
* See also #report_on_exception=.
2677+
*
2678+
* There is also a class level method to set this for all threads, see
2679+
* ::report_on_exception.
2680+
*/
2681+
2682+
static VALUE
2683+
rb_thread_report_exc(VALUE thread)
2684+
{
2685+
rb_thread_t *th;
2686+
GetThreadPtr(thread, th);
2687+
return th->report_on_exception ? Qtrue : Qfalse;
2688+
}
2689+
2690+
2691+
/*
2692+
* call-seq:
2693+
* thr.report_on_exception= boolean -> true or false
2694+
*
2695+
* When set to +true+, all threads (including the main program) will
2696+
* report the exception if an exception is raised in this +thr+.
2697+
*
2698+
* See also #report_on_exception.
2699+
*
2700+
* There is also a class level method to set this for all threads, see
2701+
* ::report_on_exception=.
2702+
*/
2703+
2704+
static VALUE
2705+
rb_thread_report_exc_set(VALUE thread, VALUE val)
2706+
{
2707+
rb_thread_t *th;
2708+
2709+
GetThreadPtr(thread, th);
2710+
th->report_on_exception = RTEST(val);
2711+
return val;
2712+
}
2713+
2714+
25962715
/*
25972716
* call-seq:
25982717
* thr.group -> thgrp or nil
@@ -4633,6 +4752,8 @@ Init_Thread(void)
46334752
rb_define_singleton_method(rb_cThread, "list", rb_thread_list, 0);
46344753
rb_define_singleton_method(rb_cThread, "abort_on_exception", rb_thread_s_abort_exc, 0);
46354754
rb_define_singleton_method(rb_cThread, "abort_on_exception=", rb_thread_s_abort_exc_set, 1);
4755+
rb_define_singleton_method(rb_cThread, "report_on_exception", rb_thread_s_report_exc, 0);
4756+
rb_define_singleton_method(rb_cThread, "report_on_exception=", rb_thread_s_report_exc_set, 1);
46364757
#if THREAD_DEBUG < 0
46374758
rb_define_singleton_method(rb_cThread, "DEBUG", rb_thread_s_debug, 0);
46384759
rb_define_singleton_method(rb_cThread, "DEBUG=", rb_thread_s_debug_set, 1);
@@ -4665,6 +4786,8 @@ Init_Thread(void)
46654786
rb_define_method(rb_cThread, "stop?", rb_thread_stop_p, 0);
46664787
rb_define_method(rb_cThread, "abort_on_exception", rb_thread_abort_exc, 0);
46674788
rb_define_method(rb_cThread, "abort_on_exception=", rb_thread_abort_exc_set, 1);
4789+
rb_define_method(rb_cThread, "report_on_exception", rb_thread_report_exc, 0);
4790+
rb_define_method(rb_cThread, "report_on_exception=", rb_thread_report_exc_set, 1);
46684791
rb_define_method(rb_cThread, "safe_level", rb_thread_safe_level, 0);
46694792
rb_define_method(rb_cThread, "group", rb_thread_group, 0);
46704793
rb_define_method(rb_cThread, "backtrace", rb_thread_backtrace_m, -1);

vm_core.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,7 @@ typedef struct rb_vm_struct {
495495

496496
unsigned int running: 1;
497497
unsigned int thread_abort_on_exception: 1;
498+
unsigned int thread_report_on_exception: 1;
498499
unsigned int trace_running: 1;
499500
volatile int sleeper;
500501

@@ -786,6 +787,7 @@ typedef struct rb_thread_struct {
786787
/* misc */
787788
enum method_missing_reason method_missing_reason: 8;
788789
unsigned int abort_on_exception: 1;
790+
unsigned int report_on_exception: 1;
789791
#ifdef USE_SIGALTSTACK
790792
void *altstack;
791793
#endif
@@ -1149,6 +1151,7 @@ void rb_threadptr_unlock_all_locking_mutexes(rb_thread_t *th);
11491151
void rb_threadptr_pending_interrupt_clear(rb_thread_t *th);
11501152
void rb_threadptr_pending_interrupt_enque(rb_thread_t *th, VALUE v);
11511153
int rb_threadptr_pending_interrupt_active_p(rb_thread_t *th);
1154+
void rb_threadptr_error_print(rb_thread_t *th, VALUE errinfo);
11521155

11531156
#define RUBY_VM_CHECK_INTS(th) ruby_vm_check_ints(th)
11541157
static inline void

0 commit comments

Comments
 (0)