Оптимистичное чтение и блокировка STM (программной транзакционной памяти) с помощью C / C ++

Я проводил некоторое исследование реализаций STM (программной транзакционной памяти), в частности алгоритмов, которые используют блокировки и не зависят от наличия сборщика мусора, чтобы поддерживать совместимость с не -управляемые языки, такие как C / C ++. Я прочитал главу о STM в книге Херлихи и Шавита «Искусство многопроцессорного программирования» , а также прочитал пару статей Шавита, в которых описываются его «Транзакционная блокировка» и ] «Транзакционная блокировка II» Реализации STM. Их основной подход заключается в использовании хэш-таблицы, в которой хранятся значения глобальных часов версии и блокировки, чтобы определить, была ли затронута область памяти при записи другого потока. Насколько я понимаю алгоритм, когда выполняется транзакция записи, часы версии считываются и сохраняются в локальной памяти потока, а набор чтения и записи также создаются в локальной памяти потока. Затем выполняются следующие шаги:

  1. Значения любых прочитанных адресов сохраняются в наборе чтения. Это означает, что транзакция проверяет, что любые считываемые ячейки не заблокированы, и они равны или меньше локально сохраненного значения часов версии.
  2. Значения любых записанных адресов сохраняются в наборе для записи вместе со значениями, которые должны быть записаны в эти места.
  3. После того, как вся транзакция записи завершена (и это может включать чтение и запись в несколько ячеек), транзакция пытается заблокировать каждый адрес, который должен быть записан, используя блокировку в хэш-таблице, которая хешируется. против значения адреса.
  4. Когда все адреса, установленные для записи, заблокированы, часы глобальной версии увеличиваются атомарно, а новое увеличенное значение сохраняется локально.
  5. Транзакция записи проверяет снова, чтобы убедиться, что значения в наборе для чтения не были обновлены с новым номером версии или не заблокированы другим потоком.
  6. Транзакция записи обновляет отметку версии для этой ячейки памяти с новым значением, которое она сохранила с шага # 4, и фиксирует значения в наборе записи в память
  7. Блокировки ячеек памяти снимаются

Если какой-либо из вышеуказанных шагов проверки завершился неудачно (то есть шаги №1, №3 и №5), то транзакция записи прерывается.

Процесс чтения-транзакции намного проще. Согласно документам Шавита, мы просто

  1. считываем и локально сохраняем глобальное значение часов версии
  2. Проверяем, чтобы в ячейках памяти не было часового значения, превышающего текущее сохраненное глобальное значение часов версии, а также убедитесь, что ячейки памяти в настоящее время не заблокированы
  3. Выполнить операции чтения
  4. Повторить этап №2 для проверки

Если один из этапов №2 или №4 терпит неудачу, то транзакция чтения прерывается.

Вопрос, который я, кажется, не могу решить в уме, заключается в том, что происходит, когда вы пытаетесь прочитать область памяти внутри объекта, который находится в куче, а другой поток вызывает delete на указатель на этот объект? В статьях Шавита они подробно объясняют, как не может быть записи в область памяти, которая была переработана или освобождена, но кажется, что внутри транзакции чтения нет ничего, что препятствовало бы возможному сценарию синхронизации, который позволил бы вам для чтения из области памяти внутри объекта, освобожденной другим потоком. В качестве примера рассмотрим следующий код:

Поток A выполняет следующее внутри атомарной транзакции чтения: connected_list_node * next_node = node-> next;

Thread B выполняет следующее: удалить узел;

Поскольку next_node является локальной переменной потока, это не транзакционный объект. Операция разыменования, необходимая для присвоения ему значения node-> next , на самом деле требует двух отдельных чтений. В промежутках между этими чтениями delete может быть вызвано на узле , так что чтение из элемента next фактически считывается из сегмента памяти, который уже был освобожден. Поскольку чтения оптимистичны, освобождение памяти, на которую указывает узел в потоке B , не будет обнаружено в потоке A .Не вызовет ли это возможный сбой или ошибку сегментации? Если да, то как этого можно избежать, не блокируя также и ячейки памяти для чтения (то, что отмечается как в учебнике, так и в статьях, не является необходимым)?

12
задан Jason 8 December 2011 в 00:41
поделиться