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

ruby-changes:23971

From: yugui <ko1@a...>
Date: Sun, 10 Jun 2012 21:51:46 +0900 (JST)
Subject: [ruby-changes:23971] yugui:r36022 (trunk): Fixes threading on NativeClient.

yugui	2012-06-10 21:51:37 +0900 (Sun, 10 Jun 2012)

  New Revision: 36022

  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=36022

  Log:
    Fixes threading on NativeClient.
    
    * thread_pthread.c (timer_thread_sleep): Extracted out a function from
      thread_timer(). Added an alternative implementation for platforms
      that lacks select(2) or pipe(2).
      (rb_thread_create_timer_thread, native_cond_initialize,
      native_cond_destroy): Replaced wrong HAVE_XXX checks.
    
    * configure.in (pthread_attr_init): New check.

  Modified files:
    trunk/ChangeLog
    trunk/configure.in
    trunk/thread_pthread.c

Index: configure.in
===================================================================
--- configure.in	(revision 36021)
+++ configure.in	(revision 36022)
@@ -2015,6 +2015,11 @@
 	thr_stksegment pthread_stackseg_np pthread_getthrds_np \
 	pthread_cond_init pthread_condattr_setclock pthread_condattr_init \
         pthread_sigmask)
+    if test "${host_os}" = "nacl"; then
+      ac_cv_func_pthread_attr_init=no
+    else
+      AC_CHECK_FUNCS(pthread_attr_init)
+    fi
 fi
 if test x"$ac_cv_header_ucontext_h" = xyes; then
     if test x"$rb_with_pthread" = xyes; then
Index: ChangeLog
===================================================================
--- ChangeLog	(revision 36021)
+++ ChangeLog	(revision 36022)
@@ -1,3 +1,15 @@
+Sun Jun 10 20:23:14 2012  Yuki Sonoda (Yugui)  <yugui@y...>
+
+	Fixes threading on NativeClient.
+
+	* thread_pthread.c (timer_thread_sleep): Extracted out a function from
+	  thread_timer(). Added an alternative implementation for platforms
+	  that lacks select(2) or pipe(2).
+	  (rb_thread_create_timer_thread, native_cond_initialize,
+	  native_cond_destroy): Replaced wrong HAVE_XXX checks.
+
+	* configure.in (pthread_attr_init): New check.
+
 Sun Jun 10 21:30:11 2012  Tanaka Akira  <akr@f...>
 
 	* process.c (rb_exec_without_timer_thread): renamed from rb_exec_err.
Index: thread_pthread.c
===================================================================
--- thread_pthread.c	(revision 36021)
+++ thread_pthread.c	(revision 36022)
@@ -52,6 +52,14 @@
 #define USE_MONOTONIC_COND 0
 #endif
 
+#ifdef __native_client__
+/* Doesn't have select(1). */
+# define USE_SLEEPY_TIMER_THREAD 0
+#else
+/* The timer thread sleeps while only one Ruby thread is running. */
+# define USE_SLEEPY_TIMER_THREAD 1
+#endif
+
 static void
 gvl_acquire_common(rb_vm_t *vm)
 {
@@ -140,11 +148,9 @@
 gvl_init(rb_vm_t *vm)
 {
     native_mutex_initialize(&vm->gvl.lock);
-#ifdef HAVE_PTHREAD_CONDATTR_INIT
     native_cond_initialize(&vm->gvl.cond, RB_CONDATTR_CLOCK_MONOTONIC);
     native_cond_initialize(&vm->gvl.switch_cond, RB_CONDATTR_CLOCK_MONOTONIC);
     native_cond_initialize(&vm->gvl.switch_wait_cond, RB_CONDATTR_CLOCK_MONOTONIC);
-#endif
     vm->gvl.acquired = 0;
     vm->gvl.waiting = 0;
     vm->gvl.need_yield = 0;
@@ -153,11 +159,9 @@
 static void
 gvl_destroy(rb_vm_t *vm)
 {
-#ifdef HAVE_PTHREAD_CONDATTR_INIT
     native_cond_destroy(&vm->gvl.switch_wait_cond);
     native_cond_destroy(&vm->gvl.switch_cond);
     native_cond_destroy(&vm->gvl.cond);
-#endif
     native_mutex_destroy(&vm->gvl.lock);
 }
 
@@ -239,19 +243,17 @@
     }
 }
 
-#ifdef HAVE_PTHREAD_CONDATTR_INIT
-int pthread_condattr_init(pthread_condattr_t *attr);
-
 static void
 native_cond_initialize(rb_thread_cond_t *cond, int flags)
 {
 #ifdef HAVE_PTHREAD_COND_INIT
     int r;
+# ifdef HAVE_PTHREAD_COND_ATTR_INIT
     pthread_condattr_t attr;
 
     pthread_condattr_init(&attr);
 
-#if USE_MONOTONIC_COND
+#  if USE_MONOTONIC_COND
     cond->clockid = CLOCK_REALTIME;
     if (flags & RB_CONDATTR_CLOCK_MONOTONIC) {
 	r = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
@@ -259,9 +261,12 @@
 	    cond->clockid = CLOCK_MONOTONIC;
 	}
     }
-#endif
+#  endif
 
     r = pthread_cond_init(&cond->cond, &attr);
+# else
+    r = pthread_cond_init(&cond->cond, NULL);
+# endif
     if (r != 0) {
 	rb_bug_errno("pthread_cond_init", r);
     }
@@ -280,7 +285,6 @@
     }
 #endif
 }
-#endif
 
 /*
  * In OS X 10.7 (Lion), pthread_cond_signal and pthread_cond_broadcast return
@@ -819,21 +823,27 @@
         th->machine_register_stack_maxsize = th->machine_stack_maxsize;
 #endif
 
+#ifdef HAVE_PTHREAD_ATTR_INIT
 	CHECK_ERR(pthread_attr_init(&attr));
 
-#ifdef PTHREAD_STACK_MIN
+# ifdef PTHREAD_STACK_MIN
 	thread_debug("create - stack size: %lu\n", (unsigned long)stack_size);
 	CHECK_ERR(pthread_attr_setstacksize(&attr, stack_size));
-#endif
+# endif
 
-#ifdef HAVE_PTHREAD_ATTR_SETINHERITSCHED
+# ifdef HAVE_PTHREAD_ATTR_SETINHERITSCHED
 	CHECK_ERR(pthread_attr_setinheritsched(&attr, PTHREAD_INHERIT_SCHED));
-#endif
+# endif
 	CHECK_ERR(pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED));
 
 	err = pthread_create(&th->thread_id, &attr, thread_start_func_1, th);
+#else
+	err = pthread_create(&th->thread_id, NULL, thread_start_func_1, th);
+#endif
 	thread_debug("create: %p (%d)\n", (void *)th, err);
+#ifdef HAVE_PTHREAD_ATTR_INIT
 	CHECK_ERR(pthread_attr_destroy(&attr));
+#endif
     }
     return err;
 }
@@ -1078,13 +1088,19 @@
 static int check_signal_thread_list(void) { return 0; }
 #endif /* USE_SIGNAL_THREAD_LIST */
 
+#define TT_DEBUG 0
+#define WRITE_CONST(fd, str) (void)(write((fd),(str),sizeof(str)-1)<0)
+
+/* 100ms.  10ms is too small for user level thread scheduling
+ * on recent Linux (tested on 2.6.35)
+ */
+#define TIME_QUANTUM_USEC (100 * 1000)
+
+#if USE_SLEEPY_TIMER_THREAD
 static int timer_thread_pipe[2] = {-1, -1};
 static int timer_thread_pipe_owner_process;
 
-#define TT_DEBUG 0
 
-#define WRITE_CONST(fd, str) (void)(write((fd),(str),sizeof(str)-1)<0)
-
 /* only use signal-safe system calls here */
 void
 rb_thread_wakeup_timer_thread(void)
@@ -1146,17 +1162,77 @@
     timer_thread_pipe[0] = timer_thread_pipe[1] = -1;
 }
 
-/* 100ms.  10ms is too small for user level thread scheduling
- * on recent Linux (tested on 2.6.35)
+/**
+ * Let the timer thread sleep a while.
+ *
+ * The timer thread sleeps until woken up by rb_thread_wakeup_timer_thread() if only one Ruby thread is running.
+ * @pre the calling context is in the timer thread.
  */
-#define TIME_QUANTUM_USEC (100 * 1000)
+static inline void
+timer_thread_sleep(rb_global_vm_lock_t* gvl)
+{
+    int result;
+    int need_polling;
+    struct timeval timeout;
+    fd_set rfds;
+    FD_ZERO(&rfds);
+    FD_SET(timer_thread_pipe[0], &rfds);
 
+    need_polling = check_signal_thread_list();
+
+    if (gvl->waiting > 0 || need_polling) {
+	timeout.tv_sec = 0;
+	timeout.tv_usec = TIME_QUANTUM_USEC;
+
+	/* polling (TIME_QUANTUM_USEC usec) */
+	result = select(timer_thread_pipe[0] + 1, &rfds, 0, 0, &timeout);
+    }
+    else {
+	/* wait (infinite) */
+	result = select(timer_thread_pipe[0] + 1, &rfds, 0, 0, 0);
+    }
+
+    if (result == 0) {
+	/* maybe timeout */
+    }
+    else if (result > 0) {
+	consume_communication_pipe();
+    }
+    else { /* result < 0 */
+	switch (errno) {
+	case EBADF:
+	case EINVAL:
+	case ENOMEM: /* from Linux man */
+	case EFAULT: /* from FreeBSD man */
+	    rb_async_bug_errno("thread_timer: select", errno);
+	default:
+	    /* ignore */;
+	}
+    }
+}
+
+#else /* USE_SLEEPY_TIMER_THREAD */
+# define PER_NANO 1000000000
+void rb_thread_wakeup_timer_thread(void) {}
+
+static pthread_mutex_t timer_thread_lock;
+static rb_thread_cond_t timer_thread_cond;
+
+static inline void
+timer_thread_sleep(rb_global_vm_lock_t* unused) {
+    struct timespec ts;
+    ts.tv_sec = 0;
+    ts.tv_nsec = TIME_QUANTUM_USEC * 1000;
+    ts = native_cond_timeout(&timer_thread_cond, ts);
+
+    native_cond_timedwait(&timer_thread_cond, &timer_thread_lock, &ts);
+}
+#endif /* USE_SLEEPY_TIMER_THREAD */
+
 static void *
 thread_timer(void *p)
 {
     rb_global_vm_lock_t *gvl = (rb_global_vm_lock_t *)p;
-    int result;
-    struct timeval timeout;
 
     if (TT_DEBUG) WRITE_CONST(2, "start timer thread\n");
 
@@ -1164,51 +1240,27 @@
     prctl(PR_SET_NAME, "ruby-timer-thr");
 #endif
 
+#if !USE_SLEEPY_TIMER_THREAD
+    native_mutex_initialize(&timer_thread_lock);
+    native_cond_initialize(&timer_thread_cond, RB_CONDATTR_CLOCK_MONOTONIC);
+    native_mutex_lock(&timer_thread_lock);
+#endif
     while (system_working > 0) {
-	fd_set rfds;
-	int need_polling;
 
 	/* timer function */
 	ping_signal_thread_list();
 	timer_thread_function(0);
-	need_polling = check_signal_thread_list();
 
 	if (TT_DEBUG) WRITE_CONST(2, "tick\n");
 
-	/* wait */
-	FD_ZERO(&rfds);
-	FD_SET(timer_thread_pipe[0], &rfds);
-
-	if (gvl->waiting > 0 || need_polling) {
-	    timeout.tv_sec = 0;
-	    timeout.tv_usec = TIME_QUANTUM_USEC;
-
-	    /* polling (TIME_QUANTUM_USEC usec) */
-	    result = select(timer_thread_pipe[0] + 1, &rfds, 0, 0, &timeout);
-	}
-	else {
-	    /* wait (infinite) */
-	    result = select(timer_thread_pipe[0] + 1, &rfds, 0, 0, 0);
-	}
-
-	if (result == 0) {
-	    /* maybe timeout */
-	}
-	else if (result > 0) {
-	    consume_communication_pipe();
-	}
-	else { /* result < 0 */
-	  switch (errno) {
-	    case EBADF:
-	    case EINVAL:
-	    case ENOMEM: /* from Linux man */
-	    case EFAULT: /* from FreeBSD man */
-	      rb_async_bug_errno("thread_timer: select", errno);
-	    default:
-	      /* ignore */;
-	  }
-	}
+        /* wait */
+	timer_thread_sleep(gvl);
     }
+#if !USE_SLEEPY_TIMER_THREAD
+    native_mutex_unlock(&timer_thread_lock);
+    native_cond_destroy(&timer_thread_cond);
+    native_mutex_destroy(&timer_thread_lock);
+#endif
 
     if (TT_DEBUG) WRITE_CONST(2, "finish timer thread\n");
     return NULL;
@@ -1217,13 +1269,16 @@
 static void
 rb_thread_create_timer_thread(void)
 {
-#ifndef __native_client__
     if (!timer_thread_id) {
+	int err;
+#ifdef HAVE_PTHREAD_ATTR_INIT
 	pthread_attr_t attr;
-	int err;
 
-	pthread_attr_init(&attr);
-#ifdef PTHREAD_STACK_MIN
+	if (pthread_attr_init(&attr)) {
+	    fprintf(stderr, "[FATAL] Failed to initialize pthread attr(errno: %d)\n", err);
+	    exit(EXIT_FAILURE);
+        }
+# ifdef PTHREAD_STACK_MIN
 	if (PTHREAD_STACK_MIN < 4096 * 3) {
 	    /* Allocate the machine stack for the timer thread
              * at least 12KB (3 pages).  FreeBSD 8.2 AMD64 causes
@@ -1236,8 +1291,10 @@
 	    pthread_attr_setstacksize(&attr,
 				      PTHREAD_STACK_MIN + (THREAD_DEBUG ? BUFSIZ : 0));
 	}
+# endif
 #endif
 
+#if USE_SLEEPY_TIMER_THREAD
 	/* communication pipe with timer thread and signal handler */
 	if (timer_thread_pipe_owner_process != getpid()) {
 	    if (timer_thread_pipe[0] != -1) {
@@ -1251,7 +1308,7 @@
 	    }
             rb_update_max_fd(timer_thread_pipe[0]);
             rb_update_max_fd(timer_thread_pipe[1]);
-#if defined(HAVE_FCNTL) && defined(F_GETFL) && defined(F_SETFL) && defined(O_NONBLOCK)
+# if defined(HAVE_FCNTL) && defined(F_GETFL) && defined(F_SETFL) && defined(O_NONBLOCK)
 	    {
 		int oflags;
 		int err;
@@ -1264,24 +1321,30 @@
 		if (err == -1)
 		    rb_sys_fail(0);
 	    }
-#endif /* defined(HAVE_FCNTL) && defined(F_GETFL) && defined(F_SETFL) */
+# endif /* defined(HAVE_FCNTL) && defined(F_GETFL) && defined(F_SETFL) */
 
 	    /* validate pipe on this process */
 	    timer_thread_pipe_owner_process = getpid();
 	}
+#endif /* USE_SLEEPY_TIMER_THREAD */
 
 	/* create timer thread */
 	if (timer_thread_id) {
 	    rb_bug("rb_thread_create_timer_thread: Timer thread was already created\n");
 	}
+#ifdef HAVE_PTHREAD_ATTR_INIT
 	err = pthread_create(&timer_thread_id, &attr, thread_timer, &GET_VM()->gvl);
+#else
+	err = pthread_create(&timer_thread_id, NULL, thread_timer, &GET_VM()->gvl);
+#endif
 	if (err != 0) {
 	    fprintf(stderr, "[FATAL] Failed to create timer thread (errno: %d)\n", err);
 	    exit(EXIT_FAILURE);
 	}
+#ifdef HAVE_PTHREAD_ATTR_INIT
 	pthread_attr_destroy(&attr);
+#endif
     }
-#endif
 }
 
 static int
@@ -1357,6 +1420,7 @@
 int
 rb_reserved_fd_p(int fd)
 {
+#if USE_SLEEPY_TIMER_THRAED
     if (fd == timer_thread_pipe[0] ||
 	fd == timer_thread_pipe[1]) {
 	return 1;
@@ -1364,6 +1428,9 @@
     else {
 	return 0;
     }
+#else
+    return 0;
+#endif
 }
 
 #endif /* THREAD_SYSTEM_DEPENDENT_IMPLEMENTATION */

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

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