diff options
author | Nobuyoshi Nakada <nobu@ruby-lang.org> | 2021-02-10 15:24:23 +0900 |
---|---|---|
committer | Nobuyoshi Nakada <nobu@ruby-lang.org> | 2021-02-10 19:44:47 +0900 |
commit | 3acc81d9e41b18380b9e0168fe2b5e5e0c727256 (patch) | |
tree | 0d68429ae285afae1b0e5a33bae2f317fb2e1b55 /missing | |
parent | ad2c7f8a1ea82d5b1913b466fcac63b77cb07569 (diff) |
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>
Notes
Notes:
Merged: https://github.com/ruby/ruby/pull/4167
Diffstat (limited to 'missing')
-rw-r--r-- | missing/dtoa.c | 13 |
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); } |