Условная многопоточная, но не однопоточная, ошибка (Common Lisp) - PullRequest
0 голосов
/ 02 мая 2019

Я сузил проблему до одного конкретного вызова функции для одной из моих библиотечных процедур, которая выглядит как (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))).

1 Ответ

1 голос
/ 08 мая 2019

Форма (delete fptr-1 (gethash key tbl)) может быть причиной, она изменяет структуру списка так, чтобы при параллельном доступе мог отображаться поврежденный список.

Каково определение операции push?Повреждение также происходит, если все операции push и все pop-операции заключены в with-lock-held (с использованием одной и той же блокировки)?

...