[ruby-cvs:71198] normal:r64107 (trunk): thread_pthread: remove timer-thread by restructuring GVL

normal at ruby-lang.org normal at ruby-lang.org
Mon Jul 30 05:47:34 JST 2018


normal	2018-07-30 05:47:33 +0900 (Mon, 30 Jul 2018)

  New Revision: 64107

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

  Log:
    thread_pthread: remove timer-thread by restructuring GVL
    
    To reduce resource use and reduce CI failure; remove
    timer-thread.  Single-threaded Ruby processes (including forked
    children) will never see extra thread overhead.  This prevents
    glibc and jemalloc from going into multi-threaded mode and
    initializing locks or causing fragmentation via arena explosion.
    
    The GVL is implements its own wait-queue as a ccan/list to
    permit controlling wakeup order.  Timeslice under contention is
    handled by a designated timer thread (similar to choosing a
    "patrol_thread" for current deadlock checking).
    
    There is only one self-pipe, now, as wakeups for timeslice are
    done independently using condition variables.  This reduces FD
    pressure slightly.
    
    Signal handling is handled directly by a Ruby Thread (instead
    of timer-thread) by exposing signal self-pipe to callers of
    rb_thread_fd_select, native_sleep, rb_wait_for_single_fd, etc...
    Acquiring, using, and releasing the self-pipe is exposed via 4
    new internal functions:
    
    1) rb_sigwait_fd_get - exclusively acquire timer_thread_pipe.normal[0]
    
    2) rb_sigwait_fd_sleep - sleep and wait for signal (and no other FDs)
    
    3) rb_sigwait_fd_put - release acquired result from rb_sigwait_fd_get
    
    4) rb_sigwait_fd_migrate - migrate signal handling to another thread
                               after calling rb_sigwait_fd_put.
    
    rb_sigwait_fd_migrate is necessary for waitpid callers because
    only one thread can wait on self-pipe at a time, otherwise a
    deadlock will occur if threads fight over the self-pipe.
    
    TRAP_INTERRUPT_MASK is now set for the main thread directly in
    signal handler via rb_thread_wakeup_timer_thread.
    
    Originally, I wanted to use POSIX timers
    (timer_create/timer_settime) for this.  Unfortunately, this
    proved unfeasible as Mutex#sleep resumes on spurious wakeups and
    test/thread/test_cv.rb::test_condvar_timed_wait failed.  Using
    pthread_sigmask to mask out SIGVTALRM fixed that test,  but
    test/fiddle/test_function.rb::test_nogvl_poll proved there'd be
    some unavoidable (and frequent) incompatibilities from that
    approach.
    
    Finally, this allows us to drop thread_destruct_lock and
    interrupt current ec directly.
    
    We don't need to rely on vm->thread_destruct_lock or a coherent
    vm->running_thread on any platform.  Separate timer-thread for
    time slice and signal handling is relegated to thread_win32.c,
    now.
    
    [ruby-core:88088] [Misc #14937]

  Modified files:
    trunk/internal.h
    trunk/process.c
    trunk/signal.c
    trunk/test/ruby/test_process.rb
    trunk/thread.c
    trunk/thread_pthread.c
    trunk/thread_pthread.h
    trunk/thread_win32.c
    trunk/vm_core.h


More information about the ruby-cvs mailing list