summaryrefslogtreecommitdiff
path: root/thread_pthread.c
diff options
context:
space:
mode:
authornormal <normal@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2018-12-20 00:07:19 +0000
committernormal <normal@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2018-12-20 00:07:19 +0000
commit0b38221d4ea75d8ac96b2adb1f7fafb0b20f9d29 (patch)
tree3f9e432e5819a1ca767720ca5886f604e067e0b4 /thread_pthread.c
parentcb3393add588ba14f95449515d5fe75eb8eada27 (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
Diffstat (limited to 'thread_pthread.c')
-rw-r--r--thread_pthread.c14
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: