diff options
author | normal <normal@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2018-12-20 00:07:19 +0000 |
---|---|---|
committer | normal <normal@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2018-12-20 00:07:19 +0000 |
commit | 0b38221d4ea75d8ac96b2adb1f7fafb0b20f9d29 (patch) | |
tree | 3f9e432e5819a1ca767720ca5886f604e067e0b4 | |
parent | cb3393add588ba14f95449515d5fe75eb8eada27 (diff) |
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
-rw-r--r-- | thread_pthread.c | 14 |
1 files 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: |