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>
This commit is contained in:
Nobuyoshi Nakada 2021-02-10 15:24:23 +09:00
Родитель ad2c7f8a1e
Коммит 3acc81d9e4
2 изменённых файлов: 21 добавлений и 3 удалений

Просмотреть файл

@ -158,6 +158,17 @@ assert_equal '[[:e1, 1], [:e2, 2]]', %q{
a #
}
# dtoa race condition
assert_equal '[:ok, :ok, :ok]', %q{
n = 3
n.times.map{
Ractor.new{
10_000.times{ rand.to_s }
:ok
}
}.map(&:take)
}
###
###
# Ractor still has several memory corruption so skip huge number of tests

Просмотреть файл

@ -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);
}