Not all CPU may have InterlockedExchange. If so then FPC likely does a CriticalSection....
On Intel/AMD InterlockedExchange is not a function call. It is a single assemble instruction.
FPC for Mac (x86) gives:
# [42] While InterlockedExchange (LCL, 1)>0 do sleep(0);
jmp Lj14
.align 2
Lj13:
xorl %edi,%edi
call _SYSUTILS_$$_SLEEP$LONGWORD
Lj14:
leaq _U_$P$THR_$$_LCL(%rip),%rdi
movl $1,%esi
call FPC_INTERLOCKEDEXCHANGE
testl %eax,%eax
ja Lj13
Suggests it is InterlockedExchange due to the Lj13 loop. (It doesn't stay down in FPC_INTERLOCKEDEXCHANGE as it would for CS).
IAC, since It's waiting for a state change, it has to come back up in order to enter the sleep(0).
Whereas for CS:
# [41] EnterCriticalSection(_F1CritLock);
leaq _U_$P$THR_$$_F1CritLock(%rip),%rdi
call _SYSTEM_$$_ENTERCRITICALSECTION$TRTLCRITICALSECTION
It stays down there until released.
There are things that InterlockedExchange can't do. Like protecting an entire System call (maybe to create/update a file).
You can do your own CS, by using InterlockedExchange, and doing a spin lock. But if the wait is longer, then a CS means your thread can sleep instead of using CPU time.
I code it as:
While InterlockedExchange (LCL, 1)>0 do sleep(0);
Where sleep (0) could also be sleep (10) or 100 or whatever - typically sleep(0) as then the threadmanager can give the CPU to whatever needs it rather than no sleep at all.
Sometimes you may need even less than an InterlockedExchange.
You may just need a read or write boundary.
And sometimes you can just normal write memory, and another thread will read it eventually.
If I'm using it it's because the some variable risks being written by different executing threads. If that variable is, for example, a counter or queue, then it must be protected.