简体   繁体   中英

How does "lock cmpxchg" work in assembly?

I came across this old (GCC prior to 4.8.3 -- bug 60272) bug report https://gcc.gnu.org/ml/gcc-bugs/2014-02/msg01951.html . This is fixed now. But I have a question regarding this. I compiled the following code snippet

#include <atomic>
struct Node { Node* next; };
void Push(std::atomic<Node*>& head, Node* node)
{
    node->next = head.load();
    while(!head.compare_exchange_weak(node->next, node))
        ;
}

void Pop(std::atomic<Node*>& head){
    for(;;){
        Node* value=head.exchange(nullptr);
        if(value){
            delete value;
            break;
        }
    }
}

with :

g++ -S -std=c++11 -pthread -O3 test.cc -o test.S

The assembly generated has the following ( I put up only the relevant portion):

.....
  4 .L4:
  5   lock cmpxchgq %rsi, (%rdi)
  6   jne .L6
  7   rep ret
  8   .p2align 4,,10
  9   .p2align 3
 10 .L6:
 11   movq  %rax, (%rsi)
 12   jmp .L4
.....

Here's my question. Let's say this code is running concurrently with 2 threads. for T1, line no 5 gets executed then T1 got interrupted and T2 does something which can potentially pop the queue to completion. When OS re-schedules T1, it'll resume from line 6 , where somebody should ** re-evalute** the condition before executing jne . But if it's not re-evaluated, then that can lead to memory corruption. Am I thinking in the right direction?

The cmpxchg instruction will set dst only if it matches eax . Otherwise it will jump to L6 which updates eax and restarts the loop. The lock prefix enables exclusive access to any memory operands of the current instruction. In other words this atomically pushes the node until it's successful. The bug was because they were not checking the results initially.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM