Понимание назначения ССВ в этом примере - PullRequest
3 голосов
/ 20 октября 2011

Я читаю Области ограниченного выполнения и другие ошибки [Брайан Грюкмейер] , пытаясь понять ограниченные области выполнения, однако у меня возникают некоторые проблемы с пониманием следующего примера:

RuntimeHelpers.PrepareConstrainedRegions();
try {
    // Prepare my backout code
    MethodInfo m = _list.GetType().GetMethod("RemoveAt", new Type[] { typeof(int) });
    RuntimeHelpers.PrepareMethod(m.MethodHandle);

    IEnumerator en = c.GetEnumerator();
    while(en.MoveNext()) {
        _list.Insert(index++, en.Current);
        // Assuming that these lines aren't reordered.
        numAdded++;
    }
    _version++;
}
catch(Exception) {
    // Reliable backout code
    while(numAdded > 0) {
        _list.RemoveAt(index--);
        numAdded--;
    }
    throw;
}

Насколько я понимаю, блок try ограничен , ограничены только блоки finally и catch. Это означает, что во время блока try асинхронное исключение (например, ThreadAbortException) может быть выдано в любое время, в частности оно может быть выдано до numAdded++, но после _list.Insert. В этом случае код возврата удалит один элемент слишком мало из _list.

Учитывая это, я изо всех сил пытаюсь понять цель ограниченной области выполнения в этом примере.

Правильно ли мое понимание этого или я что-то упустил?

1 Ответ

1 голос
/ 26 сентября 2013

Документация и фактическое поведение CER не совпадают точно на основании того, что я наблюдаю. Проблема, которую вы описываете, когда ThreadAbortException вводится между Insert и numAdded++, невозможна ни с одной из протестированных версий .NET Framework. Для этого есть две возможные причины.

  • PrepareConstrainedRegions, несмотря на то, что говорится в документации, оказывает заметное влияние на блок try. Это задержит определенные инъекции прерывания; особенно те, которые не приходят, когда поток находится в состоянии оповещения.
  • Даже при отсутствии вызова PrepareConstrainedRegions прерывание все равно не будет введено в это место. На основе кода SSCLI прерывание будет введено при обратном переходе для вращения цикла while.

Я понял кое-что из этого, когда отвечал на свой собственный вопрос здесь , а затем пытался ответить на вопрос о том, как Thread.Abort на самом деле работает здесь .

Пункт № 2 недопустим. Это деталь реализации SSCLI, которая может не переноситься на официальные дистрибутивы (хотя я подозреваю, что это действительно так). Кроме того, он игнорирует возможность ввода прерывания в какой-то момент во время выполнения Insert. Я предполагаю, что вполне возможно, что решающие биты Insert могли бы использовать CER для внутреннего использования.

Точка № 1, возможно, имеет значение, но напрашивается вопрос, почему Microsoft не документировала это и почему в цитируемой вами статье это тоже не упоминалось. Конечно, автор статьи знал об этом факте. В противном случае я тоже не понимаю, как представленный код может быть безопасным. Другими словами, сейчас это кажется безопасным только случайно.

Если бы мне нужно было угадать, что PrepareConstrainedRegions делает за кулисами, я бы сказал, что он устанавливает флаг в движке JIT, который говорит ему не вводить обработчик опроса GC, который стратегически размещается в обратной ветви. прыжки для кода внутри блока CER try. Этот хук опроса GC обычно используется для асинхронного прерывания (в дополнение к его основной цели, связанной со сборкой мусора).

...