I claim there's no UB here, neither per the language standard (the standard doesn't cover this function/intrinsic) nor per the implementation, and there's a simple rollover.
Here's my reasoning...
InterlockedIncrement()
is conceptually very simple and if it had a special case, it would be very hard to miss it and fail to document it. And the documentation hasn't mentioned any special case here for some 15+ years.
How would you implement it anyway?
If you're on the 80486 or better, the most natural implementation uses the XADD
instruction with the LOCK
prefix that atomically adds a value to a memory variable. The instruction by itself does not generate any overflow exception, however it does modify the EFLAGS
register as does the regular addition instruction, ADD
, so it's possible to detect an overflow and act on it. Specifically, you could throw in the INTO
instruction to turn the overflow condition into an exception. Or you could use the conditional jump instruction, JO
, to jump the overflow handler.
If you're on the 80386 or better, you can also use the XCHG
instruction (the LOCK
is implicit with this instruction), to make a loop that would try to atomically update a memory variable (this is how InterlockedExchange() and InterlockedCompareExchange() can be implemented, there's also a handier (for this purpose) CMPXCHG
instruction since the 80486). In this case you'd need to perform the register increment as usual, with the ADD
instruction or with the INC
instruction, and you can optionally detect any overflow condition (in EFLAGS.OF
) and handle it as mentioned earlier.
Now, would you want to throw INTO
or JO
into all instances of InterlockedIncrement()
? Probably not, definitely not by default. People like their atomic operations small and fast.
This is the "immediate" UB. What about the "creeping" UB?
If you had C code like this:
int a = INT_MAX;
if (a + 1 < a)
puts("Overflow!");
You'd likely get nothing printed nowadays.
Modern compilers know that a + 1
can't legally(!) overflow and so the condition in the if statement can be taken as false irrespective of the value of a
.
Can you have a similar optimization with InterlockedIncrement()
?
Well, given that the variable is volatile
and can indeed change in a different thread at any moment, the compiler may not assume unchanged a
from two memory reads of it (you'd likely write a + 1 < a
or similar as multiple statements and each a
would need to be fetched if it's volatile).
It would also be an odd context to try to make the optimization in.