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

ruby-changes:43323

From: naruse <ko1@a...>
Date: Mon, 13 Jun 2016 21:34:11 +0900 (JST)
Subject: [ruby-changes:43323] naruse:r55397 (trunk): * thread.c (debug_deadlock_check): show thread lock dependency and

naruse	2016-06-13 21:34:06 +0900 (Mon, 13 Jun 2016)

  New Revision: 55397

  https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=55397

  Log:
    * thread.c (debug_deadlock_check): show thread lock dependency and
      backtrace [Feature #8214] [ruby-dev:47217]
    
    * thread.c (thread_status_name): show "sleep_forever" instead of
      "sleep" if called from inspect.

  Modified files:
    trunk/ChangeLog
    trunk/NEWS
    trunk/thread.c
Index: NEWS
===================================================================
--- NEWS	(revision 55396)
+++ NEWS	(revision 55397)
@@ -167,3 +167,5 @@ with all sufficient information, see the https://github.com/ruby/ruby/blob/trunk/NEWS#L167
   be <= 0x100, and Array#max and min must not be redefined.  It will work
   in most casual and real-life use case where it is written with intent
   to `Math.max(x, y)`.
+
+* Thread deadlock detection now shows their backtrace and dependency. [Feature #8214]
Index: thread.c
===================================================================
--- thread.c	(revision 55396)
+++ thread.c	(revision 55397)
@@ -2737,7 +2737,7 @@ rb_thread_group(VALUE thread) https://github.com/ruby/ruby/blob/trunk/thread.c#L2737
 }
 
 static const char *
-thread_status_name(rb_thread_t *th)
+thread_status_name(rb_thread_t *th, int detail)
 {
     switch (th->status) {
       case THREAD_RUNNABLE:
@@ -2745,8 +2745,9 @@ thread_status_name(rb_thread_t *th) https://github.com/ruby/ruby/blob/trunk/thread.c#L2745
 	    return "aborting";
 	else
 	    return "run";
-      case THREAD_STOPPED:
       case THREAD_STOPPED_FOREVER:
+	if (detail) return "sleep_forever";
+      case THREAD_STOPPED:
 	return "sleep";
       case THREAD_KILLED:
 	return "dead";
@@ -2806,7 +2807,7 @@ rb_thread_status(VALUE thread) https://github.com/ruby/ruby/blob/trunk/thread.c#L2807
 	}
 	return Qfalse;
     }
-    return rb_str_new2(thread_status_name(th));
+    return rb_str_new2(thread_status_name(th, FALSE));
 }
 
 
@@ -2952,7 +2953,7 @@ rb_thread_inspect(VALUE thread) https://github.com/ruby/ruby/blob/trunk/thread.c#L2953
     VALUE str;
 
     GetThreadPtr(thread, th);
-    status = thread_status_name(th);
+    status = thread_status_name(th, TRUE);
     str = rb_sprintf("#<%"PRIsVALUE":%p", cname, (void *)thread);
     if (!NIL_P(th->name)) {
 	rb_str_catf(str, "@%"PRIsVALUE, th->name);
@@ -4850,27 +4851,46 @@ ruby_native_thread_p(void) https://github.com/ruby/ruby/blob/trunk/thread.c#L4851
     return th != 0;
 }
 
+VALUE rb_vm_backtrace_str_ary(rb_thread_t *th, long lev, long n);
 static void
-debug_deadlock_check(rb_vm_t *vm)
+debug_deadlock_check(rb_vm_t *vm, VALUE msg)
 {
-#ifdef DEBUG_DEADLOCK_CHECK
     rb_thread_t *th = 0;
+    VALUE sep = rb_str_new_cstr("\n   ");
 
-    printf("%d %d %p %p\n", vm_living_thread_num(vm), vm->sleeper, GET_THREAD(), vm->main_thread);
+    rb_str_catf(msg, "\n%d threads, %d sleeps current:%p main thread:%p\n",
+	    vm_living_thread_num(vm), vm->sleeper, GET_THREAD(), vm->main_thread);
     list_for_each(&vm->living_threads, th, vmlt_node) {
-	printf("th:%p %d %d", th, th->status, th->interrupt_flag);
 	if (th->locking_mutex) {
 	    rb_mutex_t *mutex;
+	    struct rb_thread_struct volatile *mth;
+	    int waiting;
 	    GetMutexPtr(th->locking_mutex, mutex);
 
 	    native_mutex_lock(&mutex->lock);
-	    printf(" %p %d\n", mutex->th, mutex->cond_waiting);
+	    mth = mutex->th;
+	    waiting = mutex->cond_waiting;
 	    native_mutex_unlock(&mutex->lock);
+	    rb_str_catf(msg, "* %+"PRIsVALUE"\n   rb_thread_t:%p native:%p int:%u mutex:%p cond:%d\n",
+		    th->self, th, th->thread_id,
+		    th->interrupt_flag, mth, waiting);
 	}
-	else
-	    puts("");
+	else {
+	    rb_str_catf(msg, "* %+"PRIsVALUE"\n   rb_thread_t:%p native:%p int:%u\n",
+		    th->self, th, th->thread_id,
+		    th->interrupt_flag);
+	}
+	{
+	    rb_thread_list_t *list = th->join_list;
+	    while (list) {
+		rb_str_catf(msg, "    depended by: tb_thread_id:%p\n", list->th);
+		list = list->next;
+	    }
+	}
+	rb_str_catf(msg, "   ");
+	rb_str_concat(msg, rb_ary_join(rb_vm_backtrace_str_ary(th, 0, 0), sep));
+	rb_str_catf(msg, "\n");
     }
-#endif
 }
 
 static void
@@ -4905,7 +4925,7 @@ rb_check_deadlock(rb_vm_t *vm) https://github.com/ruby/ruby/blob/trunk/thread.c#L4925
 	VALUE argv[2];
 	argv[0] = rb_eFatal;
 	argv[1] = rb_str_new2("No live threads left. Deadlock?");
-	debug_deadlock_check(vm);
+	debug_deadlock_check(vm, argv[1]);
 	vm->sleeper--;
 	rb_threadptr_raise(vm->main_thread, 2, argv);
     }
Index: ChangeLog
===================================================================
--- ChangeLog	(revision 55396)
+++ ChangeLog	(revision 55397)
@@ -1,3 +1,11 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1
+Mon Jun 13 21:09:40 2016  NARUSE, Yui  <naruse@r...>
+
+	* thread.c (debug_deadlock_check): show thread lock dependency and
+	  backtrace [Feature #8214] [ruby-dev:47217]
+
+	* thread.c (thread_status_name): show "sleep_forever" instead of
+	  "sleep" if called from inspect.
+
 Mon Jun 13 20:50:07 2016  Nobuyoshi Nakada  <nobu@r...>
 
 	* parse.y (reg_named_capture_assign_iter): remove named capture

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

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