Я утверждаю, что здесь нет UB, ни в соответствии со стандартом языка (стандарт не распространяется на эту функцию / встроенную функцию), ни в соответствии с реализацией, и есть простой переход.
Вот мои рассуждения ...
InterlockedIncrement()
концептуально очень прост, и если бы у него был особый случай, было бы очень трудно пропустить его и не задокументировать его.И в документации не упоминалось ни одного особого случая здесь в течение более 15 лет.
Как бы вы это реализовали?
Если у вас 80486 или лучше, самая естественная реализацияиспользует инструкцию XADD
с префиксом LOCK
, который атомарно добавляет значение в переменную памяти.Сама по себе инструкция не генерирует никаких исключений переполнения, однако она изменяет регистр EFLAGS
, как и инструкция обычного сложения ADD
, поэтому можно обнаружить переполнение и действовать в соответствии с ним.В частности, вы можете добавить команду INTO
, чтобы превратить условие переполнения в исключение.Или вы можете использовать команду условного перехода JO
, чтобы перейти к обработчику переполнения.
Если вы используете 80386 или выше, вы также можете использовать инструкцию XCHG
(LOCK
- этонеявно с этой инструкцией), чтобы создать цикл, который попытался бы атомарно обновить переменную памяти (так могут быть реализованы InterlockedExchange () и InterlockedCompareExchange (), есть также более удобная (для этой цели) CMPXCHG
инструкция, начиная с 80486).В этом случае вам нужно выполнить увеличение регистра как обычно, с помощью инструкции ADD
или с помощью инструкции INC
, и вы можете опционально обнаружить любое условие переполнения (в EFLAGS.OF
) и обработать его, как упомянуто ранее.
Теперь, вы бы хотели бросить INTO
или JO
во все случаи InterlockedIncrement()
?Наверное, нет, определенно не по умолчанию.Людям нравятся их атомные операции, маленькие и быстрые.
Это «немедленный» UB.Как насчет "ползучего" UB?Если бы у вас был такой код C:
int a = INT_MAX;
if (a + 1 < a)
puts("Overflow!");
В настоящее время вы, скорее всего, получите ничего не напечатанное .Современные компиляторы знают, что a + 1
не может юридически (!) Переполниться, и поэтому условие в операторе if может быть взято как ложное независимо от значения a
.
Можете ли вы провести аналогичную оптимизацию сInterlockedIncrement()
?
Что ж, учитывая, что переменная равна volatile
и может действительно измениться в другой поток в любой момент, компилятор не может принять неизменным a
из двух чтений из памяти (вы 'Скорее всего, мы напишем a + 1 < a
или аналогичные как несколько операторов, и каждый a
должен будет быть выбран, если он изменчив).
Также было бы странным контекстом попытаться выполнить оптимизацию.