From 0b38221d4ea75d8ac96b2adb1f7fafb0b20f9d29 Mon Sep 17 00:00:00 2001 From: normal Date: Thu, 20 Dec 2018 00:07:19 +0000 Subject: thread_pthread.c (ubf_timer_disarm): ignore EINVAL iff timer is dead The following race may happen if ubf_timer_destroy calls timer_delete before ubf_timer_disarm gets called from a different thread. Consider the following timelines: ubf_timer_destroy | ubf_timer_disarm -------------------------------------+----------------------------- | CAS(ARM => DISARM) CAS(DISARM => DEAD) | timer_delete | | timer_settime(disarm) Another option may be to add an intermediate "RTIMER_DISARMING" state to the transition, but I figure the EINVAL check is simpler and less intrusive code-wise. cf. http://ci.rvm.jp/results/trunk-iseq_binary@silicon-docker/1545794 git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@66457 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- thread_pthread.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/thread_pthread.c b/thread_pthread.c index b087d461c7..e074a854f5 100644 --- a/thread_pthread.c +++ b/thread_pthread.c @@ -1769,8 +1769,18 @@ ubf_timer_disarm(void) case RTIMER_DISARM: return; /* likely */ case RTIMER_ARMING: return; /* ubf_timer_arm will disarm itself */ case RTIMER_ARMED: - if (timer_settime(timer_posix.timerid, 0, &zero, 0)) - rb_bug_errno("timer_settime (disarm)", errno); + if (timer_settime(timer_posix.timerid, 0, &zero, 0)) { + int err = errno; + + if (err == EINVAL) { + prev = ATOMIC_CAS(timer_posix.state, RTIMER_DISARM, RTIMER_DISARM); + + /* main thread may have killed the timer */ + if (prev == RTIMER_DEAD) return; + + rb_bug_errno("timer_settime (disarm)", err); + } + } return; case RTIMER_DEAD: return; /* stay dead */ default: -- cgit v1.2.3