The Intel documentation says
This instruction can be used with a
LOCK
prefix to allow the instruction to be executed atomically.
My question is
Can
CMPXCHG
operate with memory address? From the document it seems not but can anyone confirm that only works with actual VALUE in registers, not memory address?If
CMPXCHG
isn't atomic and a high level language level CAS has to be implemented throughLOCK CMPXCHG
(withLOCK
prefix), what's the purpose of introducing such an instruction at all?
You are mixing up high-level locks with the low-level CPU feature that happened to be named
LOCK
.The high-level locks that lock-free algorithms try to avoid can guard arbitrary code fragments whose execution may take arbitrary time and thus, these locks will have to put threads into wait state until the lock is available which is a costly operation, e.g. implies maintaining a queue of waiting threads.
This is an entirely different thing than the CPU
LOCK
prefix feature which guards a single instruction only and thus might hold other threads for the duration of that single instruction only. Since this is implemented by the CPU itself, it doesn’t require additional software efforts.Therefore the challenge of developing lock-free algorithms is not the removal of synchronization entirely, it boils down to reduce the critical section of the code to a single atomic operation which will be provided by the CPU itself.
The LOCK prefix is to lock the memory access for the current command, so that other commands that are in the CPU pipeline can access the memory at this time. Using the LOCK prefix, the execution of the command won't be interrupted by another command in the CPU pipeline due to memory access of other commands that are executed at the same time. The INTEL manual says:
It seems like part what you're really asking is:
The simple answer (that others have given) is simply that Intel designed it this way. But this leads to the question:
On a single-CPU system,
cmpxchg
is atomic with respect to other threads, or any other code running on the same CPU core. (But not to "system" observers like a memory-mapped I/O device, or a device doing DMA reads of normal memory, solock cmpxchg
was relevant even on uniprocessor CPU designs).Context switches can only happen on interrupts, and interrupts happen before or after an instruction, not in the middle. Any code running on the same CPU will see the
cmpxchg
as either fully executed or not at all.For example, the Linux kernel is normally compiled with SMP support, so it uses
lock cmpxchg
for atomic CAS. But when booted on a single-processor system, it will patch thelock
prefix to anop
everywhere that code was inlined, sincenop
cmpxchg
runs much faster thanlock cmpxchg
. For more info, see this LWN article about Linux's "SMP alternatives" system. It can even patch back tolock
prefixes before hot-plugging a second CPU.Read more about atomicity of single instructions on uniprocessor systems in this answer, and in @supercat's answer + comments on Can
num++
be atomic forint num
. See my answer there for lots of details about how atomicity really works / is implemented for read-modify-write instructions likelock cmpxchg
.(This same reasoning also applies to
cmpxchg8b
/cmpxchg16b
, andxadd
, which usually only used for synchonization / atomic ops, not to make single-threaded code run faster. Obviously memory-destinationadd [mem], reg
is useful outside of thelock add [mem], reg
case.)