From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on dcvr.yhbt.net X-Spam-Level: X-Spam-ASN: X-Spam-Status: No, score=-4.0 required=3.0 tests=ALL_TRUSTED,BAYES_00 shortcircuit=no autolearn=ham autolearn_force=no version=3.4.0 Received: from localhost (dcvr.yhbt.net [127.0.0.1]) by dcvr.yhbt.net (Postfix) with ESMTP id EBBA41F404; Sun, 29 Apr 2018 10:50:29 +0000 (UTC) Date: Sun, 29 Apr 2018 10:50:29 +0000 From: Eric Wong To: spew@80x24.org Subject: [PATCH 4/2] thread_pthread (native_sleep): perform GC if idle Message-ID: <20180429105029.GA23412@dcvr> References: <20180429035007.6499-1-e@80x24.org> MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Disposition: inline In-Reply-To: <20180429035007.6499-1-e@80x24.org> List-Id: It still makes sense to perform GC while using native_sleep for blocked Queue (or other operations with only "local" dependencies). This is because some GVL release operations can still leave other threads sleeping, as in the example below. The following goes from to 90MB to roughly 41MB depending on entropy: require 'thread' q = SizedQueue.new(128) nr = 100_000 th = Thread.new do File.open('/dev/urandom') do |rd| nr.times do q.push(rd.read(16384)) end end end nr.times { q.pop } th.join --- thread_pthread.c | 81 +++++++++++++++++++++++++++++++----------------- 1 file changed, 53 insertions(+), 28 deletions(-) diff --git a/thread_pthread.c b/thread_pthread.c index fccac48a44..ccc2ccb52e 100644 --- a/thread_pthread.c +++ b/thread_pthread.c @@ -35,6 +35,7 @@ #include #endif +static int timespec_cmp(const struct timespec *a, const struct timespec *b); void rb_native_mutex_lock(rb_nativethread_lock_t *lock); void rb_native_mutex_unlock(rb_nativethread_lock_t *lock); static int native_mutex_trylock(rb_nativethread_lock_t *lock); @@ -349,17 +350,23 @@ native_cond_timedwait(rb_nativethread_cond_t *cond, pthread_mutex_t *mutex, cons return r; } -static struct timespec -native_cond_timeout(rb_nativethread_cond_t *cond, struct timespec timeout_rel) +static void +condattr_now(struct timespec *ts) { - struct timespec abs; - if (condattr_monotonic) { - getclockofday(&abs); + getclockofday(ts); } else { - rb_timespec_now(&abs); + rb_timespec_now(ts); } +} + +static struct timespec +native_cond_timeout(rb_nativethread_cond_t *cond, struct timespec timeout_rel) +{ + struct timespec abs; + + condattr_now(&abs); timespec_add(&abs, &timeout_rel); return abs; @@ -1026,12 +1033,23 @@ ubf_pthread_cond_signal(void *ptr) rb_native_cond_signal(&th->native_thread_data.sleep_cond); } +static int +cond_expired_p(const struct timespec *end) +{ + struct timespec now; + + condattr_now(&now); + return timespec_cmp(&now, end) >= 0; +} + static void native_sleep(rb_thread_t *th, struct timespec *timeout_rel) { struct timespec timeout; rb_nativethread_lock_t *lock = &th->interrupt_lock; rb_nativethread_cond_t *cond = &th->native_thread_data.sleep_cond; + int intr; + int do_gc = 1; if (timeout_rel) { /* Solaris cond_timedwait() return EINVAL if an argument is greater than @@ -1050,28 +1068,35 @@ native_sleep(rb_thread_t *th, struct timespec *timeout_rel) timeout = native_cond_timeout(cond, *timeout_rel); } - GVL_UNLOCK_BEGIN(); - { - rb_native_mutex_lock(lock); - th->unblock.func = ubf_pthread_cond_signal; - th->unblock.arg = th; - - if (RUBY_VM_INTERRUPTED(th->ec)) { - /* interrupted. return immediate */ - thread_debug("native_sleep: interrupted before sleep\n"); - } - else { - if (!timeout_rel) - rb_native_cond_wait(cond, lock); - else - native_cond_timedwait(cond, lock, &timeout); - } - th->unblock.func = 0; - th->unblock.arg = 0; - - rb_native_mutex_unlock(lock); - } - GVL_UNLOCK_END(); + do { + if (!do_gc || gvl_contended_p(th->vm)) { + GVL_UNLOCK_BEGIN(); + { + rb_native_mutex_lock(lock); + th->unblock.func = ubf_pthread_cond_signal; + th->unblock.arg = th; + + if ((intr = RUBY_VM_INTERRUPTED(th->ec))) { + /* interrupted. return immediate */ + thread_debug("native_sleep: interrupted before sleep\n"); + } + else { + if (!timeout_rel) + rb_native_cond_wait(cond, lock); + else + native_cond_timedwait(cond, lock, &timeout); + } + th->unblock.func = 0; + th->unblock.arg = 0; + + rb_native_mutex_unlock(lock); + } + GVL_UNLOCK_END(); + } + else if (!(intr = RUBY_VM_INTERRUPTED(th->ec))) { + do_gc = rb_gc_step(th->ec); + } + } while (!intr && (!timeout_rel || !cond_expired_p(&timeout))); thread_debug("native_sleep done\n"); } -- EW