Я сузил проблему до одного конкретного вызова функции для одной из моих библиотечных процедур, которая выглядит как (pop-hstack current-hstack)
, которая извлекает элемент из структуры стека.Это вызывает повреждение данных (несоответствие, см. Ниже) в структуре стека, но только при работе нескольких потоков.Я попытался обернуть вызов в блокировку, например, (bt:with-lock-held (*lock*) (pop-hstack current-hstack)
, но current-hstack все еще становится несовместимым где-то во время выполнения, когда активны два или более потоков.Аргументы для pop-hstack (например, current-hstack) в каждом потоке являются динамически связанными специальными переменными и, таким образом, не разделяются между потоками.Это сбивает с толку, если несоответствие вводится многопоточностью (без несогласованности, запущенной однопоточным), или, возможно, ошибкой условного программирования в определении структуры или функции pop-hstack.
(defstruct hstack
"An hstack (hash stack) is an expanded stack representation containing an
adjustable one-dimensional array of elements, plus a hash table for quickly
determining if an element is in the stack. Keyfn is applied to elements to
access the hash table. New elements are pushed at the fill-pointer, and
popped at the fill-pointer minus 1."
(vector (make-array 0 :adjustable t :fill-pointer t) :type (array * (*)))
(table (make-hash-table) :type hash-table) ;can take a custom hash table
(keyfn #'identity :type function)) ;fn to get hash table key for an element
(defun pop-hstack (hstk)
"Pops an element from hstack's vector. Also removes the element's index from
the element's hash table entry--and the entry itself if it's the last index."
(let* ((vec (hstack-vector hstk))
(fptr-1 (1- (fill-pointer vec)))
(tbl (hstack-table hstk))
(key (funcall (hstack-keyfn hstk) (aref vec fptr-1))))
(when (null (setf (gethash key tbl) (delete fptr-1 (gethash key tbl))))
(remhash key tbl))
(vector-pop vec)))
Обычно, hstack'sвектор стека и хэш-таблица синхронизированы и содержат одинаковое количество записей: (length (hstack-vector x)) = (hash-table-count (hstack-table x))
.Только когда в hstack есть повторяющиеся элементы, количество записей будет отличаться.(Потому что тогда одна запись в хеш-таблице будет содержать несколько векторных индексов для повторяющихся элементов, появляющихся в векторе.) Однако несоответствие между количеством записей в векторе и хеш-таблице все еще проявляется, когда нет дублирующих элементов.Как правило, в хэш-таблице есть один или два дополнительных элемента, указывающих на то, что эти дополнительные элементы не были должным образом удалены во время операции pop-hstack.Кажется, что вектор стека всегда содержит правильные элементы.
EDIT (5/2/19): исправлена ошибка кодирования в pop-hstack: замените (delete fptr-1 (gethash key tbl))
на (setf (gethash key tbl) (delete fptr-1 (gethash key tbl)))
.