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

ruby-changes:54160

From: normal <ko1@a...>
Date: Thu, 13 Dec 2018 18:25:52 +0900 (JST)
Subject: [ruby-changes:54160] normal:r66381 (trunk): thread_pthread.c (native_sleep): sched_yield if GVL uncontended

normal	2018-12-13 18:25:46 +0900 (Thu, 13 Dec 2018)

  New Revision: 66381

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

  Log:
    thread_pthread.c (native_sleep): sched_yield if GVL uncontended
    
    Uncontended GVL waitqueue could mean a single CPU setup where
    threads are starved and can't even insert themselves into our
    waitqueue.  So we force other threads to run upon releasing
    the GVL in an uncontended state, in the hope that we can
    avoid entering the slow path of ppoll and similar syscalls.
    
    This should prevent test/ruby/test_thread.rb::test_signal_at_join
    timeout problems on our single CPU FreeBSD CI machine.
    
    [ruby-core:90417] [Bug #15398]

  Modified files:
    trunk/thread_pthread.c
Index: thread_pthread.c
===================================================================
--- thread_pthread.c	(revision 66380)
+++ thread_pthread.c	(revision 66381)
@@ -2016,6 +2016,27 @@ ubf_ppoll_sleep(void *ignore) https://github.com/ruby/ruby/blob/trunk/thread_pthread.c#L2016
 }
 
 /*
+ * Single CPU setups benefit from explicit sched_yield() before ppoll(),
+ * since threads may be too starved to enter the GVL waitqueue for
+ * us to detect contention.  Instead, we want to kick other threads
+ * so they can run and possibly prevent us from entering slow paths
+ * in ppoll() or similar syscalls.
+ *
+ * Confirmed on FreeBSD 11.2 and Linux 4.19.
+ * [ruby-core:90417] [Bug #15398]
+ */
+#define GVL_UNLOCK_BEGIN_YIELD(th) do { \
+    const native_thread_data_t *next; \
+    rb_vm_t *vm = th->vm; \
+    RB_GC_SAVE_MACHINE_CONTEXT(th); \
+    rb_native_mutex_lock(&vm->gvl.lock); \
+    next = gvl_release_common(vm); \
+    rb_native_mutex_unlock(&vm->gvl.lock); \
+    if (!next && vm_living_thread_num(vm) > 1) { \
+        native_thread_yield(); \
+    }
+
+/*
  * This function does not exclusively acquire sigwait_fd, so it
  * cannot safely read from it.  However, it can be woken up in
  * 4 ways:
@@ -2032,7 +2053,8 @@ native_ppoll_sleep(rb_thread_t *th, rb_h https://github.com/ruby/ruby/blob/trunk/thread_pthread.c#L2053
     th->unblock.func = ubf_ppoll_sleep;
     rb_native_mutex_unlock(&th->interrupt_lock);
 
-    GVL_UNLOCK_BEGIN(th);
+    GVL_UNLOCK_BEGIN_YIELD(th);
+
     if (!RUBY_VM_INTERRUPTED(th->ec)) {
         struct pollfd pfd[2];
         struct timespec ts;
@@ -2066,7 +2088,7 @@ native_sleep(rb_thread_t *th, rb_hrtime_ https://github.com/ruby/ruby/blob/trunk/thread_pthread.c#L2088
         th->unblock.func = ubf_sigwait;
         rb_native_mutex_unlock(&th->interrupt_lock);
 
-        GVL_UNLOCK_BEGIN(th);
+        GVL_UNLOCK_BEGIN_YIELD(th);
 
         if (!RUBY_VM_INTERRUPTED(th->ec)) {
             rb_sigwait_sleep(th, sigwait_fd, rel);

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

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