diff options
author | normal <normal@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2018-07-29 20:47:33 +0000 |
---|---|---|
committer | normal <normal@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2018-07-29 20:47:33 +0000 |
commit | 708bfd21156828526fe72de2cedecfaca6647dc1 (patch) | |
tree | 3a29a7e3edf47618e8cf8f1159d27993e12e66ce /thread_win32.c | |
parent | 822e54a527b6be1f4cda7c48cf723e02b51fadc9 (diff) | |
download | bundler-708bfd21156828526fe72de2cedecfaca6647dc1.tar.gz |
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]
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@64107 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'thread_win32.c')
-rw-r--r-- | thread_win32.c | 27 |
1 files changed, 25 insertions, 2 deletions
diff --git a/thread_win32.c b/thread_win32.c index 2d5eac1ff4..3770200d30 100644 --- a/thread_win32.c +++ b/thread_win32.c @@ -20,6 +20,7 @@ #define native_thread_yield() Sleep(0) #define unregister_ubf_list(th) +#define ubf_wakeup_all_threads() do {} while (0) static volatile DWORD ruby_native_thread_key = TLS_OUT_OF_INDEXES; @@ -680,18 +681,21 @@ static struct { static unsigned long __stdcall timer_thread_func(void *dummy) { + rb_vm_t *vm = GET_VM(); thread_debug("timer_thread\n"); rb_w32_set_thread_description(GetCurrentThread(), L"ruby-timer-thread"); while (WaitForSingleObject(timer_thread.lock, TIME_QUANTUM_USEC/1000) == WAIT_TIMEOUT) { - timer_thread_function(dummy); + timer_thread_function(); + ruby_sigchld_handler(vm); /* probably no-op */ + rb_threadptr_check_signal(vm->main_thread); } thread_debug("timer killed\n"); return 0; } void -rb_thread_wakeup_timer_thread(void) +rb_thread_wakeup_timer_thread(int sig) { /* do nothing */ } @@ -768,6 +772,25 @@ rb_reserved_fd_p(int fd) return 0; } +int +rb_sigwait_fd_get(rb_thread_t *th) +{ + return -1; /* TODO */ +} + +void +rb_sigwait_fd_put(rb_thread_t *th, int fd) +{ + rb_bug("not implemented, should not be called"); +} + + +void +rb_sigwait_sleep(const rb_thread_t *th, int fd, const struct timespec *ts) +{ + rb_bug("not implemented, should not be called"); +} + rb_nativethread_id_t rb_nativethread_self(void) { |