[前][次][番号順一覧][スレッド一覧]

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/

[前][次][番号順一覧][スレッド一覧]