Я реализовал неизменную карту ha sh и сопровождающий контейнер STM, вдохновленный atom
clojure, то есть чем-то похожим на std::unique_ptr
C ++, в котором он управляет (но не обязательно владеет) другим объектом через указатель, который можно передавать и заменять, и только уменьшая счетчик ссылок на управляемый объект, а не уничтожая его напрямую. При запуске некоторого тестового кода, скомпилированного с -O3
, я начал замечать неверные результаты.
Код для сравнения и обмена выглядит примерно так:
hashmap *update(stm *stm, update_callback cb, void *user_args)
{
while (1) {
lock(stm->mutex);
hashmap *current = stm->reference;
increment_ref_count(current); // Line 6
unlock(stm->mutex);
hashmap *aspirant = cb(current, user_args); // returns a new, thread local instance
increment_ref_count(aspirant);
// Position 1
lock(stm->mutex);
if (current == stm->reference) { // success, no modification while we were busy
stm->reference = aspirant;
increment_ref_count(aspirant); // stm now has a reference
unlock(stm->mutex);
// Position 2.1
decrement_ref_count(current); // release reference acquired at line 6
decrement_ref_count(current); // stm no longer has a reference
return aspirant;
} else { // reference was modified, loop and try again
unlock(stm->mutex);
// Position 2.2
decrement_ref_count(current); // release reference acquired at line 6
decrement_ref_count(aspirant); // ref_count now at zero, aspirant is free'd
}
}
}
increment_
& decrement_ref_count
вводит / уменьшает счетчик ссылок на хэш-карту атомарно. Если в результате уменьшения значение счетчика упадет до нуля, hasmap будет освобожден вскоре после этого.
Задача создания контейнера STM для указателя с подсчетом ссылок в основном заключалась в создании aquiring-the-reference -and-incrementing the-counter atomi c, поэтому я использую здесь блокировки.
В качестве теста я использую hashmap + STM для подсчета вхождений в списке слов.
Если я запускаю код, указанный здесь, условия гонки не возникают. Теперь возникает моя проблема : если я переместу decrement_ref_count(current); // for line 6
из if/else
, из Positions 2.1/2.2
(сразу после второго заблокированного региона) и поставлю его на Position 1
(прямо перед вторым заблокирован регион), внезапно подсчет слов начинает становиться неправильным, и я понятия не имею, почему.
Мой аргумент таков: а) я не использую current
во второй критической области, и б ) поэтому не должно иметь значения, выпускаю ли я ссылку до или после сравнения и обмена.
Очевидно, у меня есть обходной путь / решение проблемы; просто оставив decrement
там, где они сейчас, но мне бы очень хотелось понять, почему это происходит.
Скомпилировано с: gcc -Wall -Wcast-align -Wswitch-enum -Wswitch-default -Winit-self -pedantic -O3 -DNDEBUG -std=gnu11
в подсистеме Windows для Linux.