aboutsummaryrefslogtreecommitdiffstats
path: root/missing
diff options
context:
space:
mode:
authorNobuyoshi Nakada <nobu@ruby-lang.org>2021-02-10 15:24:23 +0900
committerNobuyoshi Nakada <nobu@ruby-lang.org>2021-02-10 19:44:47 +0900
commit3acc81d9e41b18380b9e0168fe2b5e5e0c727256 (patch)
tree0d68429ae285afae1b0e5a33bae2f317fb2e1b55 /missing
parentad2c7f8a1ea82d5b1913b466fcac63b77cb07569 (diff)
downloadruby-3acc81d9e41b18380b9e0168fe2b5e5e0c727256.tar.gz
Fixed race in dtoa [Bug #17612]
Fixed the race condition when replacing `freelist` entry with its chained next element. At acquiring an entry, hold the entry once with the special value, then release by replacing it with the next element again after acquired. If another thread is holding the same entry at that time, spinning until the entry gets released. Co-Authored-By: Koichi Sasada <ko1@atdot.net>
Diffstat (limited to 'missing')
-rw-r--r--missing/dtoa.c13
1 files changed, 10 insertions, 3 deletions
diff --git a/missing/dtoa.c b/missing/dtoa.c
index 41b0a221d1..a940eabd91 100644
--- a/missing/dtoa.c
+++ b/missing/dtoa.c
@@ -526,6 +526,8 @@ typedef struct Bigint Bigint;
static Bigint *freelist[Kmax+1];
+#define BLOCKING_BIGINT ((Bigint *)(-1))
+
static Bigint *
Balloc(int k)
{
@@ -541,8 +543,10 @@ Balloc(int k)
rv = freelist[k];
while (rv) {
Bigint *rvn = rv;
- rv = ATOMIC_PTR_CAS(freelist[k], rv, rv->next);
- if (LIKELY(rvn == rv)) {
+ rv = ATOMIC_PTR_CAS(freelist[k], rv, BLOCKING_BIGINT);
+ if (LIKELY(rv != BLOCKING_BIGINT && rvn == rv)) {
+ rvn = ATOMIC_PTR_CAS(freelist[k], BLOCKING_BIGINT, rv->next);
+ assert(rvn == BLOCKING_BIGINT);
ASSUME(rv);
break;
}
@@ -589,7 +593,10 @@ Bfree(Bigint *v)
}
ACQUIRE_DTOA_LOCK(0);
do {
- vn = v->next = freelist[v->k];
+ do {
+ vn = ATOMIC_PTR_CAS(freelist[v->k], 0, 0);
+ } while (UNLIKELY(vn == BLOCKING_BIGINT));
+ v->next = vn;
} while (UNLIKELY(ATOMIC_PTR_CAS(freelist[v->k], vn, v) != vn));
FREE_DTOA_LOCK(0);
}