Skip to content

Гонка при параллельном чтении значения системного перечисления #1734

Description

@Stivo182

Описание

При первом одновременном обращении из фоновых заданий к одному значению системного перечисления иногда возникает System.IndexOutOfRangeException.

В примере используется НаправлениеПоиска.СКонца.

Минимальный пример

Процедура ПрочитатьЗначениеПеречисления() Экспорт

    Приостановить(100);
    Значение = НаправлениеПоиска.СКонца;

КонецПроцедуры

КоличествоЗаданий = 1000;
Менеджер = Новый МенеджерФоновыхЗаданий;
Задания = Новый Массив;

Для НомерЗадания = 1 По КоличествоЗаданий Цикл
    Задания.Добавить(
        Менеджер.Выполнить(
            ЭтотОбъект,
            "ПрочитатьЗначениеПеречисления",
            ,
            Истина
        )
    );
КонецЦикла;

Менеджер.ОжидатьВсе(Задания);

Для Каждого Задание Из Задания Цикл
    Если Задание.ИнформацияОбОшибке <> Неопределено Тогда
        Сообщить(Задание.ИнформацияОбОшибке.ПодробноеОписаниеОшибки());
        Прервать;
    КонецЕсли;
КонецЦикла;

Ожидаемое поведение

Все фоновые задания успешно получают значение НаправлениеПоиска.СКонца.

Фактическое поведение

System.IndexOutOfRangeException: Index was outside the bounds of the array.
   at System.Collections.Generic.HashSet`1.AddIfNotPresent(T value, Int32& location)
   at System.Collections.Generic.HashSet`1.Add(T item)
   at ScriptEngine.Machine.Contexts.EnumerationContext.WarnDeprecation(Int32 propNum)
   at ScriptEngine.Machine.Contexts.EnumerationContext.GetPropValueInternal(Int32 propNum)
   at ScriptEngine.Machine.Contexts.EnumerationContext.GetPropValue(Int32 propNum)

Ошибка возникает на строке:

Значение = НаправлениеПоиска.СКонца;

Предполагаемая причина

В EnumerationContext используется общее поле:

private readonly HashSet<int> _checkedDeprecatedProps = new HashSet<int>();

Метод WarnDeprecation() сначала вызывает Contains(), а затем Add() без синхронизации:

private void WarnDeprecation(int propNum)
{
    if (_checkedDeprecatedProps.Contains(propNum))
        return;

    if (GetPropertyInfo(propNum) is SystemPropertyInfo { IsDeprecated: true })
    {
        SystemLogger.Write(
            $"Обращение к устаревшему свойству {GetPropertyInfo(propNum).Name}.");
    }

    _checkedDeprecatedProps.Add(propNum);
}

При одновременном первом чтении свойства несколько фоновых заданий изменяют один экземпляр HashSet<int>. HashSet<T> не поддерживает конкурентную запись, что приводит к сбою внутри AddIfNotPresent().

Исходный код:
https://github.com/EvilBeaver/OneScript/blob/v2.2.0/src/ScriptEngine/Machine/Contexts/EnumerationContext.cs

Окружение

  • OneScript: 2.2.0
  • ОС: Windows Server 2022 Standard, 64-разрядная
  • версия ОС: 10.0.20348
  • установленный .NET Host: 9.0.5, x64

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions