From: Mark Somerville Date: 2011-06-13T19:25:45+09:00 Subject: [ruby-core:37080] Re: [Request for Comment] avoid timer thread --jy6Sn24JjFx/iggw Content-Type: multipart/mixed; boundary="LyciRD1jyfeSSjG0" Content-Disposition: inline --LyciRD1jyfeSSjG0 Content-Type: text/plain; charset=iso-8859-1 Content-Disposition: inline Content-Transfer-Encoding: quoted-printable On Sat, Jun 11, 2011 at 05:57:11AM +0900, Eric Wong wrote: > Mark Somerville wrote: > > On Tue, Feb 08, 2011 at 09:24:13PM +0900, Mark Somerville wrote: > > > On Mon, Nov 29, 2010 at 11:53:03AM +0900, SASADA Koichi wrote: > > > > I attached a patch to avoid timer thread. Your reviews are welcome. > > >=20 > > > I *finally* got around to testing this patch. > > >=20 > > > I applied this to trunk (it doesn't quite apply cleanly, but is easy = to > > > fix). All the Ruby tests pass. I also ran the tests in all of my 1.9 > > > compatible projects - everything was happy! > > >=20 > > > Most importantly, it fixes the wakeups/context switching problem nice= ly! > >=20 > > Has anyone else looked at this patch yet? >=20 > Mind reposting your updated patch? ML archives mangled the original > one. I can host a branch for it on git://bogomips.org/ruby.git too The patch I was testing was Koichi's. My earlier patch for this problem was shown to have problems. I've attached Koichi's patch again. Unfortunately it doesn't apply cleanly to master and I haven't got the experience/time needed to get it to a working state. If you or someone else does, I'll be very happy to test it out or help in any other way I can. =20 > > It would be great to get this (or something like it) into 1.9.3. > >=20 > > I consider this a pretty bad regression from 1.8. I don't want to have > > to use 1.8 any more! >=20 > I completely agree. I plan on running Ruby 1.9 daemons on my laptop > more; so having this in 1.9.3 would be great. I'm in the same position - wanting to run tens of Ruby processes for a long time on my laptop. --LyciRD1jyfeSSjG0 Content-Type: text/plain; charset=iso-8859-1 Content-Disposition: attachment; filename="avoid_timerthread.patch" Content-Transfer-Encoding: quoted-printable Index: thread_pthread.c =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- thread_pthread.c (revision 29964) +++ thread_pthread.c (working copy) @@ -11,6 +11,7 @@ =20 #ifdef THREAD_SYSTEM_DEPENDENT_IMPLEMENTATION =20 +#include #include "gc.h" =20 #ifdef HAVE_SYS_RESOURCE_H @@ -48,6 +49,20 @@ gvl_show_waiting_threads(rb_vm_t *vm) } } =20 +static void +native_set_timer(long usec) +{ + struct itimerval it; + int r; + + it.it_interval.tv_sec =3D 0; + it.it_interval.tv_usec =3D usec; /* 10ms */ + it.it_value.tv_sec =3D 0; + it.it_value.tv_usec =3D usec; + r =3D setitimer(ITIMER_VIRTUAL, &it, 0); + if (r !=3D 0) rb_bug_errno("setitimer", r); +} + #if !GVL_SIMPLE_LOCK static void gvl_waiting_push(rb_vm_t *vm, rb_thread_t *th) @@ -64,6 +79,11 @@ gvl_waiting_push(rb_vm_t *vm, rb_thread_ } th =3D vm->gvl.waiting_threads; vm->gvl.waiting++; + + if (vm->gvl.waiting =3D=3D 1) { + /* invoke timer */ + native_set_timer(10000 /* 10ms */); + } } =20 static void @@ -71,6 +91,11 @@ gvl_waiting_shift(rb_vm_t *vm, rb_thread { vm->gvl.waiting_threads =3D vm->gvl.waiting_threads->native_thread_dat= a.gvl_next; vm->gvl.waiting--; + + if (vm->gvl.waiting =3D=3D 0) { + /* stop timer */ + native_set_timer(0); + } } #endif =20 @@ -85,7 +110,6 @@ gvl_acquire(rb_vm_t *vm, rb_thread_t *th if (GVL_DEBUG) fprintf(stderr, "gvl acquire (%p): sleep\n", th); gvl_waiting_push(vm, th); if (GVL_DEBUG) gvl_show_waiting_threads(vm); - while (vm->gvl.acquired !=3D 0 || vm->gvl.waiting_threads !=3D th) { native_cond_wait(&th->native_thread_data.gvl_cond, &vm->gvl.lock); } @@ -297,9 +321,10 @@ static rb_thread_lock_t signal_thread_li static pthread_key_t ruby_native_thread_key; =20 static void -null_func(int i) +vtalrm_handler(int i) { - /* null */ + rb_vm_t *vm =3D GET_VM(); /* TODO: fix me for Multi-VM */ + RUBY_VM_SET_TIMER_INTERRUPT(vm->running_thread); } =20 static rb_thread_t * @@ -314,7 +339,14 @@ ruby_thread_set_native(rb_thread_t *th) return pthread_setspecific(ruby_native_thread_key, th) =3D=3D 0; } =20 -static void native_thread_init(rb_thread_t *th); +static void +native_thread_init(rb_thread_t *th) +{ + native_cond_initialize(&th->native_thread_data.sleep_cond); + native_cond_initialize(&th->native_thread_data.gvl_cond); + ruby_thread_set_native(th); + posix_signal(SIGVTALRM, vtalrm_handler); +} =20 void Init_native_thread(void) @@ -325,15 +357,6 @@ Init_native_thread(void) th->thread_id =3D pthread_self(); native_thread_init(th); native_mutex_initialize(&signal_thread_list_lock); - posix_signal(SIGVTALRM, null_func); -} - -static void -native_thread_init(rb_thread_t *th) -{ - native_cond_initialize(&th->native_thread_data.sleep_cond); - native_cond_initialize(&th->native_thread_data.gvl_cond); - ruby_thread_set_native(th); } =20 static void @@ -536,8 +559,6 @@ thread_start_func_1(void *th_ptr) return 0; } =20 -void rb_thread_create_control_thread(void); - struct cached_thread_entry { volatile rb_thread_t **th_area; pthread_cond_t *cond; @@ -816,6 +837,8 @@ struct signal_thread_list { struct signal_thread_list *next; }; =20 +sem_t interrupt_sem; + #ifndef __CYGWIN__ static struct signal_thread_list signal_thread_list_anchor =3D { 0, 0, 0, @@ -870,6 +893,7 @@ add_signal_thread_list(rb_thread_t *th) th->native_thread_data.signal_thread_list =3D list; }); } + sem_post(&interrupt_sem); } #endif =20 @@ -896,9 +920,9 @@ remove_signal_thread_list(rb_thread_t *t } } =20 -static pthread_t timer_thread_id; -static pthread_cond_t timer_thread_cond =3D PTHREAD_COND_INITIALIZER; -static pthread_mutex_t timer_thread_lock =3D PTHREAD_MUTEX_INITIALIZER; +static pthread_t interrupt_thread_id; +static pthread_cond_t interrupt_thread_cond =3D PTHREAD_COND_INITIALIZER; +static pthread_mutex_t interrupt_thread_lock =3D PTHREAD_MUTEX_INITIALIZER; =20 static struct timespec * get_ts(struct timespec *ts, unsigned long nsec) @@ -915,22 +939,40 @@ get_ts(struct timespec *ts, unsigned lon } =20 static void * -thread_timer(void *dummy) +interrupt_thread_func(void *dummy) { struct timespec ts; =20 - native_mutex_lock(&timer_thread_lock); - native_cond_broadcast(&timer_thread_cond); -#define WAIT_FOR_10MS() native_cond_timedwait(&timer_thread_cond, &timer_t= hread_lock, get_ts(&ts, PER_NANO/100)) + /* sync with creater thread */ + native_mutex_lock(&interrupt_thread_lock); + native_cond_broadcast(&interrupt_thread_cond); + native_mutex_unlock(&interrupt_thread_lock); + while (system_working > 0) { - int err =3D WAIT_FOR_10MS(); - if (err =3D=3D ETIMEDOUT); - else if (err =3D=3D 0 || err =3D=3D EINTR) { - if (rb_signal_buff_size() =3D=3D 0) break; +#if !defined(__CYGWIN__) && !defined(__SYMBIAN32__) + if (signal_thread_list_anchor.next) { + struct timespec ts; + sem_timedwait(&interrupt_sem, get_ts(&ts, PER_NANO/100)); + } + else +#endif + sem_wait(&interrupt_sem); + + switch (errno) { + case EINVAL: + case EDEADLK: + rb_bug_errno("interrupt_thread_core/sem_wait or sem_timedwait", errno); + } + + if (rb_signal_buff_size() > 0) { + /* signal deliverly */ + rb_vm_t *vm =3D GET_VM(); + rb_threadptr_check_signal(vm->main_thread); } - else rb_bug_errno("thread_timer/timedwait", err); =20 #if !defined(__CYGWIN__) && !defined(__SYMBIAN32__) + /* Send a signal to interrupt specific ruby thread */ + /* to cancel blocking region */ if (signal_thread_list_anchor.next) { FGLOCK(&signal_thread_list_lock, { struct signal_thread_list *list; @@ -942,59 +984,59 @@ thread_timer(void *dummy) }); } #endif - timer_thread_function(dummy); } - native_mutex_unlock(&timer_thread_lock); return NULL; } =20 -static void -rb_thread_create_timer_thread(void) +/* called from signal handler */ +void +rb_thread_set_signal(void) { - rb_enable_interrupt(); + sem_post(&interrupt_sem); +} =20 - if (!timer_thread_id) { +static void +rb_thread_create_interrupt_thread(void) +{ + if (!interrupt_thread_id) { pthread_attr_t attr; int err; =20 + sem_init(&interrupt_sem, 0, 0); + pthread_attr_init(&attr); #ifdef PTHREAD_STACK_MIN pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN + (THREAD_DEBUG ? BUFSIZ : 0)); #endif - native_mutex_lock(&timer_thread_lock); - err =3D pthread_create(&timer_thread_id, &attr, thread_timer, 0); + native_mutex_lock(&interrupt_thread_lock); + err =3D pthread_create(&interrupt_thread_id, &attr, interrupt_thread_func= , 0); if (err !=3D 0) { - native_mutex_unlock(&timer_thread_lock); + native_mutex_unlock(&interrupt_thread_lock); fprintf(stderr, "[FATAL] Failed to create timer thread (errno: %d)\n"= , err); exit(EXIT_FAILURE); } - native_cond_wait(&timer_thread_cond, &timer_thread_lock); - native_mutex_unlock(&timer_thread_lock); + + native_cond_wait(&interrupt_thread_cond, &interrupt_thread_lock); + native_mutex_unlock(&interrupt_thread_lock); } rb_disable_interrupt(); /* only timer thread recieve signal */ } =20 static int -native_stop_timer_thread(void) +native_stop_interrupt_thread(void) { int stopped; - native_mutex_lock(&timer_thread_lock); stopped =3D --system_working <=3D 0; - if (stopped) { - native_cond_signal(&timer_thread_cond); - } - native_mutex_unlock(&timer_thread_lock); - if (stopped) { - native_thread_join(timer_thread_id); - } + sem_post(&interrupt_sem); + native_thread_join(interrupt_thread_id); return stopped; } =20 static void -native_reset_timer_thread(void) +native_reset_interrupt_thread(void) { - timer_thread_id =3D 0; + interrupt_thread_id =3D 0; } =20 #ifdef HAVE_SIGALTSTACK Index: vm_core.h =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- vm_core.h (revision 29961) +++ vm_core.h (working copy) @@ -641,9 +641,10 @@ VALUE rb_vm_make_proc(rb_thread_t *th, c VALUE rb_vm_make_env_object(rb_thread_t *th, rb_control_frame_t *cfp); void rb_vm_gvl_destroy(rb_vm_t *vm); =20 -void rb_thread_start_timer_thread(void); -void rb_thread_stop_timer_thread(void); -void rb_thread_reset_timer_thread(void); +void rb_thread_start_interrupt_thread(void); +void rb_thread_stop_interrupt_thread(void); +void rb_thread_reset_interrupt_thread(void); + void *rb_thread_call_with_gvl(void *(*func)(void *), void *data1); int ruby_thread_has_gvl_p(void); VALUE rb_make_backtrace(void); Index: thread.c =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- thread.c (revision 29961) +++ thread.c (working copy) @@ -193,7 +193,6 @@ rb_thread_s_debug_set(VALUE self, VALUE=20 #endif NOINLINE(static int thread_start_func_2(rb_thread_t *th, VALUE *stack_star= t, VALUE *register_stack_start)); -static void timer_thread_function(void *); =20 #if defined(_WIN32) #include "thread_win32.c" @@ -367,7 +366,7 @@ rb_thread_terminate_all(void) } POP_TAG(); } - rb_thread_stop_timer_thread(); + rb_thread_stop_interrupt_thread(); } =20 static void @@ -2684,48 +2683,25 @@ rb_threadptr_check_signal(rb_thread_t *m } } =20 -static void -timer_thread_function(void *arg) -{ - rb_vm_t *vm =3D GET_VM(); /* TODO: fix me for Multi-VM */ - - /* for time slice */ - RUBY_VM_SET_TIMER_INTERRUPT(vm->running_thread); - - /* check signal */ - rb_threadptr_check_signal(vm->main_thread); - -#if 0 - /* prove profiler */ - if (vm->prove_profile.enable) { - rb_thread_t *th =3D vm->running_thread; - - if (vm->during_gc) { - /* GC prove profiling */ - } - } -#endif -} - void -rb_thread_stop_timer_thread(void) +rb_thread_stop_interrupt_thread(void) { - if (timer_thread_id && native_stop_timer_thread()) { - native_reset_timer_thread(); + if (interrupt_thread_id && native_stop_interrupt_thread()) { + native_reset_interrupt_thread(); } } =20 void -rb_thread_reset_timer_thread(void) +rb_thread_reset_interrupt_thread(void) { - native_reset_timer_thread(); + native_reset_interrupt_thread(); } =20 void -rb_thread_start_timer_thread(void) +rb_thread_start_interrupt_thread(void) { system_working =3D 1; - rb_thread_create_timer_thread(); + rb_thread_create_interrupt_thread(); } =20 static int @@ -4389,7 +4365,7 @@ Init_Thread(void) } } =20 - rb_thread_create_timer_thread(); + rb_thread_create_interrupt_thread(); =20 (void)native_mutex_trylock; } Index: eval.c =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- eval.c (revision 29961) +++ eval.c (working copy) @@ -35,7 +35,6 @@ VALUE rb_eSysStackError; /* initialize ruby */ =20 void rb_clear_trace_func(void); -void rb_thread_stop_timer_thread(void); =20 void rb_call_inits(void); void Init_heap(void); @@ -118,8 +117,6 @@ ruby_finalize(void) ruby_finalize_1(); } =20 -void rb_thread_stop_timer_thread(void); - int ruby_cleanup(volatile int ex) { @@ -160,7 +157,7 @@ ruby_cleanup(volatile int ex) ex =3D error_handle(ex); ruby_finalize_1(); POP_TAG(); - rb_thread_stop_timer_thread(); + rb_thread_stop_interrupt_thread(); =20 #if EXIT_SUCCESS !=3D 0 || EXIT_FAILURE !=3D 1 switch (ex) { Index: process.c =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- process.c (revision 29961) +++ process.c (working copy) @@ -989,16 +989,12 @@ proc_detach(VALUE obj, VALUE pid) char *strtok(); #endif =20 -void rb_thread_stop_timer_thread(void); -void rb_thread_start_timer_thread(void); -void rb_thread_reset_timer_thread(void); - static int forked_child =3D 0; =20 #define before_exec() \ - (rb_enable_interrupt(), (void)(forked_child ? 0 : (rb_thread_stop_time= r_thread(), 1))) + (rb_enable_interrupt(), (void)(forked_child ? 0 : (rb_thread_stop_inte= rrupt_thread(), 1))) #define after_exec() \ - (rb_thread_reset_timer_thread(), rb_thread_start_timer_thread(), forked_= child =3D 0, rb_disable_interrupt()) + (rb_thread_reset_interrupt_thread(), rb_thread_start_interrupt_thread(),= forked_child =3D 0, rb_disable_interrupt()) #define before_fork() before_exec() #define after_fork() (GET_THREAD()->thrown_errinfo =3D 0, after_exec()) =20 Index: signal.c =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- signal.c (revision 29961) +++ signal.c (working copy) @@ -522,11 +522,15 @@ ruby_nativethread_signal(int signum, sig #endif #endif =20 +void rb_thread_set_signal(void); + static RETSIGTYPE sighandler(int sig) { ATOMIC_INC(signal_buff.cnt[sig]); ATOMIC_INC(signal_buff.size); + rb_thread_set_signal(); + #if !defined(BSD_SIGNAL) && !defined(POSIX_SIGNAL) ruby_signal(sig, sighandler); #endif @@ -556,8 +560,6 @@ rb_disable_interrupt(void) #if USE_TRAP_MASK sigset_t mask; sigfillset(&mask); - sigdelset(&mask, SIGVTALRM); - sigdelset(&mask, SIGSEGV); pthread_sigmask(SIG_SETMASK, &mask, NULL); #endif } @@ -1057,6 +1059,44 @@ int ruby_enable_coredump =3D 0; #define ruby_enable_coredump 0 #endif =20 +#if HAVE_PTHREAD +#include +#include + +static VALUE +sigset2hash(sigset_t *set) +{ + VALUE hash =3D rb_hash_new(); + const struct signals *sigs; + + for (sigs =3D siglist; sigs->signm; sigs++) { + if (sigs->signo =3D=3D 0) continue; + rb_hash_aset(hash, rb_str_new2(sigs->signm), sigismember(set, sigs->signo= ) ? Qtrue : Qfalse); + fprintf(stderr, "signal: SIG%-6s is %d\n", sigs->signm, sigismember(set, = sigs->signo)); + } + + return hash; +} + +static VALUE +sigmask_of_current_thread(VALUE klass) +{ + sigset_t oldset; + int r; + r =3D pthread_sigmask(0, NULL, &oldset); + if (r !=3D 0) {rb_bug_errno("pthread_sigmask", r);} + return sigset2hash(&oldset); +} + +static VALUE +sigmask_of_current_process(VALUE klass) +{ + sigset_t oldset; + sigprocmask(0, NULL, &oldset); + return sigset2hash(&oldset); +} +#endif + /* * Many operating systems allow signals to be sent to running * processes. Some signals have a defined effect on the process, while @@ -1102,6 +1142,11 @@ Init_signal(void) rb_define_global_function("trap", sig_trap, -1); rb_define_module_function(mSignal, "trap", sig_trap, -1); rb_define_module_function(mSignal, "list", sig_list, 0); + +#if HAVE_PTHREAD + rb_define_module_function(mSignal, "sigmask_of_current_thread", sigmas= k_of_current_thread, 0); + rb_define_module_function(mSignal, "sigmask_of_current_process", sigma= sk_of_current_process, 0); +#endif =20 rb_define_method(rb_eSignal, "initialize", esignal_init, -1); rb_define_method(rb_eSignal, "signo", esignal_signo, 0); Mon Nov 29 11:38:42 2010 Koichi Sasada * eval.c:=20 * eval.c (ruby_cleanup):=20 * eval.c (ruby_finalize):=20 * process.c:=20 * process.c (proc_detach):=20 * signal.c:=20 * signal.c (Init_signal):=20 * signal.c (rb_disable_interrupt):=20 * signal.c (ruby_nativethread_signal):=20 * thread.c:=20 * thread.c (Init_Thread):=20 * thread.c (rb_thread_s_debug_set):=20 * thread.c (rb_thread_terminate_all):=20 * thread.c (rb_threadptr_check_signal):=20 * thread_pthread.c:=20 * thread_pthread.c (Init_native_thread):=20 * thread_pthread.c (add_signal_thread_list):=20 * thread_pthread.c (get_ts):=20 * thread_pthread.c (gvl_acquire):=20 * thread_pthread.c (gvl_show_waiting_threads):=20 * thread_pthread.c (gvl_waiting_push):=20 * thread_pthread.c (gvl_waiting_shift):=20 * thread_pthread.c (remove_signal_thread_list):=20 * thread_pthread.c (ruby_thread_set_native):=20 * thread_pthread.c (thread_start_func_1):=20 * thread_pthread.c (thread_timer):=20 * vm_core.h:=20 * vm_core.h (rb_vm_make_proc):=20 --LyciRD1jyfeSSjG0-- --jy6Sn24JjFx/iggw Content-Type: application/pgp-signature -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) iEYEARECAAYFAk315fIACgkQFC+QcFj+6g3RBwCfUAxRlP3yalcL049tb7NXRmOZ pIQAnRfCZcKvXcnYhgvEjEnCZL6NVgl3 =mS74 -----END PGP SIGNATURE----- --jy6Sn24JjFx/iggw--