I'm using InterlockedExchange in Windows and I have two questions that put together are basically my title question.
InterlockedExchange uses type LONG (32-bits). According to Microsoft's documentation Interlocked Variable Access: "simple reads and writes to 32-bit variables are atomic operations without InterlockedExchange". According to function documentation InterlockedExchange: "This function is atomic with respect to calls to other interlocked functions". Is it or is it not atomic to read/write a LONG in Windows without the interlocked functions?
I am reviewing some code in which one thread sets a variable then all further accesses on that variable by that thread or any number of threads it created use InterlockedExchange. To make it simple consider thread running main()
creates a thread running other()
:
LONG foo;
main()
{
foo = TRUE;
createthread(other());
/* do other things */
if(InterlockedExchange(&foo, 0))
{
cleanup()
}
}
other()
{
/* do other things */
if(InterlockedExchange(&foo, 0))
{
cleanup()
}
}
cleanup()
{
/* it's expected this is only called once */
}
Is it possible in this example that foo
will not appear TRUE to either of the InterlockedExchange calls? If main is busy doing other things so that the first InterlockedExchange call is made from the other thread, does that mean foo is guaranteed to have been written by the main thread and visible to the other thread at that time?
Sorry if this is unclear I don't know how to phrase it better.
Is it or is it not atomic to read/write a LONG in Windows without the interlocked functions?
Yes to both, assuming default alignment. This follows from the quoted statement "simple reads and writes to properly-aligned 32-bit variables are atomic operations", because LONG
is a 32-bit signed integer in Windows, regardless of the bitness of the OS.
Is it possible in this example that foo will not appear TRUE to either of the InterlockedExchange calls?
No, not possible if both calls are reached. That's because within a single thread the foo = TRUE;
write is guaranteed to be visible to the InterlockedExchange
call that comes after it. So the InterlockedExchange
call in main
will see either the TRUE
value previously set in main
, or the FALSE
value reset in the other
thread. Therefore, one of the InterlockedExchange
calls must read the foo
value as TRUE
.
However, if the /* do other things */
code in main
is an infinite loop while(1);
then that would leave only one InterlockedExchange
outstanding in other
, and it may be possible for that call to see foo
as FALSE
, for the same reason as...
If main is busy doing other things so that the first InterlockedExchange call is made from the other thread, does that mean foo is guaranteed to have been written by the main thread and visible to the other thread at that time?
Not necessarily. The foo = TRUE;
write is visible to the main
thread at the point the secondary thread is created, but may not necessarily be visible to the other
thread when it starts, or even when it gets to the InterlockedExchange
call.