mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
thread_pthread.c (native_sleep): sched_yield if GVL uncontended
Uncontended GVL waitqueue could mean a single CPU setup where threads are starved and can't even insert themselves into our waitqueue. So we force other threads to run upon releasing the GVL in an uncontended state, in the hope that we can avoid entering the slow path of ppoll and similar syscalls. This should prevent test/ruby/test_thread.rb::test_signal_at_join timeout problems on our single CPU FreeBSD CI machine. [ruby-core:90417] [Bug #15398] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@66381 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
608310d6ff
commit
89db85f716
1 changed files with 24 additions and 2 deletions
|
@ -2015,6 +2015,27 @@ ubf_ppoll_sleep(void *ignore)
|
||||||
rb_thread_wakeup_timer_thread_fd(signal_self_pipe.ub_main[1]);
|
rb_thread_wakeup_timer_thread_fd(signal_self_pipe.ub_main[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Single CPU setups benefit from explicit sched_yield() before ppoll(),
|
||||||
|
* since threads may be too starved to enter the GVL waitqueue for
|
||||||
|
* us to detect contention. Instead, we want to kick other threads
|
||||||
|
* so they can run and possibly prevent us from entering slow paths
|
||||||
|
* in ppoll() or similar syscalls.
|
||||||
|
*
|
||||||
|
* Confirmed on FreeBSD 11.2 and Linux 4.19.
|
||||||
|
* [ruby-core:90417] [Bug #15398]
|
||||||
|
*/
|
||||||
|
#define GVL_UNLOCK_BEGIN_YIELD(th) do { \
|
||||||
|
const native_thread_data_t *next; \
|
||||||
|
rb_vm_t *vm = th->vm; \
|
||||||
|
RB_GC_SAVE_MACHINE_CONTEXT(th); \
|
||||||
|
rb_native_mutex_lock(&vm->gvl.lock); \
|
||||||
|
next = gvl_release_common(vm); \
|
||||||
|
rb_native_mutex_unlock(&vm->gvl.lock); \
|
||||||
|
if (!next && vm_living_thread_num(vm) > 1) { \
|
||||||
|
native_thread_yield(); \
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This function does not exclusively acquire sigwait_fd, so it
|
* This function does not exclusively acquire sigwait_fd, so it
|
||||||
* cannot safely read from it. However, it can be woken up in
|
* cannot safely read from it. However, it can be woken up in
|
||||||
|
@ -2032,7 +2053,8 @@ native_ppoll_sleep(rb_thread_t *th, rb_hrtime_t *rel)
|
||||||
th->unblock.func = ubf_ppoll_sleep;
|
th->unblock.func = ubf_ppoll_sleep;
|
||||||
rb_native_mutex_unlock(&th->interrupt_lock);
|
rb_native_mutex_unlock(&th->interrupt_lock);
|
||||||
|
|
||||||
GVL_UNLOCK_BEGIN(th);
|
GVL_UNLOCK_BEGIN_YIELD(th);
|
||||||
|
|
||||||
if (!RUBY_VM_INTERRUPTED(th->ec)) {
|
if (!RUBY_VM_INTERRUPTED(th->ec)) {
|
||||||
struct pollfd pfd[2];
|
struct pollfd pfd[2];
|
||||||
struct timespec ts;
|
struct timespec ts;
|
||||||
|
@ -2066,7 +2088,7 @@ native_sleep(rb_thread_t *th, rb_hrtime_t *rel)
|
||||||
th->unblock.func = ubf_sigwait;
|
th->unblock.func = ubf_sigwait;
|
||||||
rb_native_mutex_unlock(&th->interrupt_lock);
|
rb_native_mutex_unlock(&th->interrupt_lock);
|
||||||
|
|
||||||
GVL_UNLOCK_BEGIN(th);
|
GVL_UNLOCK_BEGIN_YIELD(th);
|
||||||
|
|
||||||
if (!RUBY_VM_INTERRUPTED(th->ec)) {
|
if (!RUBY_VM_INTERRUPTED(th->ec)) {
|
||||||
rb_sigwait_sleep(th, sigwait_fd, rel);
|
rb_sigwait_sleep(th, sigwait_fd, rel);
|
||||||
|
|
Loading…
Reference in a new issue