From d1f1ab1fc6bd80aabde23768534c3f698503a9d8 Mon Sep 17 00:00:00 2001
From: Eric Wong <e@80x24.org>
Date: Tue, 29 Dec 2015 10:48:18 +0000
Subject: [PATCH] thread_pthread.c (rb_thread_create_timer_thread): fix race

This fixes an occasional [ASYNC BUG] failure in
bootstraptest/test_fork.rb '[ruby-dev:37934]'
which tests fork/pthread_create failure by setting
RLIMIT_NPROC to 1 and triggering EAGAIN on pthread_create
when attempting to recreate the timer thread.

The problem timeline is as follows:

thread 1                           thread 2
---------------------------------------------------------------
rb_thread_create_timer_thread
setup_communication_pipe
                                   rb_thread_wakeup_timer_thread_low
pthread_create fails               pipe looks valid, write!
CLOSE_INVALIDATE (x4)              EBADF -> ASYNC BUG

The checks in rb_thread_wakeup_timer_thread_low only tried to
guarantee proper ordering with native_stop_timer_thread, not
rb_thread_create_timer_thread :x

Now, this should allow rb_thread_create_timer_thread to
synchronize properly with rb_thread_wakeup_timer_thread_low by
delaying the validation marking of the timer_thread_pipe until
we are certain the timer thread is alive.

In this version, rb_thread_wakeup_timer_thread_low becomes a
noop.  Threading is still completely broken with NPROC==1, but
there's not much we can do about it beside warn the user.
We no longer spew a scary [ASYNC BUG] message at them and
dump core on them.
---
 thread_pthread.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/thread_pthread.c b/thread_pthread.c
index 2a15816..46cc571 100644
--- a/thread_pthread.c
+++ b/thread_pthread.c
@@ -1406,8 +1406,6 @@ setup_communication_pipe(void)
 	return e;
     }
 
-    /* validate pipe on this process */
-    timer_thread_pipe.owner_process = getpid();
     return 0;
 }
 
@@ -1615,6 +1613,9 @@ rb_thread_create_timer_thread(void)
 #endif
 	    return;
 	}
+
+	/* validate pipe on this process */
+	timer_thread_pipe.owner_process = getpid();
 	timer_thread.created = 1;
 #ifdef HAVE_PTHREAD_ATTR_INIT
 	pthread_attr_destroy(&attr);
-- 
EW

