ruby-changes:54236
From: normal <ko1@a...>
Date: Thu, 20 Dec 2018 09:07:24 +0900 (JST)
Subject: [ruby-changes:54236] normal:r66457 (trunk): thread_pthread.c (ubf_timer_disarm): ignore EINVAL iff timer is dead
normal 2018-12-20 09:07:19 +0900 (Thu, 20 Dec 2018) New Revision: 66457 https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=66457 Log: 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 Modified files: trunk/thread_pthread.c Index: thread_pthread.c =================================================================== --- thread_pthread.c (revision 66456) +++ thread_pthread.c (revision 66457) @@ -1769,8 +1769,18 @@ ubf_timer_disarm(void) https://github.com/ruby/ruby/blob/trunk/thread_pthread.c#L1769 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: -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/