// File: shreyas-gopalakrishna[P]Concurrent-Skip-list/skip_list.cpp
// Function: SkipList::add(int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>)
bool SkipList::add(int key, string value) {

    // Get the level until which the new node must be available
    int top_level = get_random_level();

    // Initialization of references of the predecessors and successors
    vector<Node*> preds(max_level + 1); 
    vector<Node*> succs(max_level + 1);

    for (size_t i = 0; i < preds.size(); i++){
        preds[i] = NULL;
        succs[i] = NULL;
    }

    // Keep trying to insert the element into the list. In case predecessors and successors are changed,
    // this loop helps to try the insert again
    while(true){
        
        // Find the predecessors and successors of where the key must be inserted
        int found = find(key, preds, succs);

        // If found and marked, wait and continue insert
        // If found and unmarked, wait until it is fully_linked and return. No insert needed
        // If not found, go ahead with insert
        if(found != -1){
            Node* node_found = succs[found];
            
            if(!node_found->marked){
                while(! node_found->fully_linked){
                }
                return false;
            }
            continue;
        }

        // Store all the Nodes which lock we acquire in a map
        // Map used so that we don't try to acquire lock to a Node we have already acquired
        // This may happen when we have the same predecessor at different levels
        map<Node*, int> locked_nodes;

        // Traverse the skip list and try to acquire the lock of predecessor at every level
        try{
            Node* pred;
            Node* succ;

            // Used to check if the predecessor and successors are same from when we tried to read them before
            bool valid = true;

            for (int level = 0; valid && (level <= top_level); level++){
                pred = preds[level];
                succ = succs[level];

                // If not already acquired lock, then acquire the lock 
                if(!(locked_nodes.count( pred ))){
                    pred->lock();
                    locked_nodes.insert(make_pair(pred, 1));
                }

                // If predecessor marked or if the predecessor and successors change, then abort and try again
                valid = !(pred->marked.load(std::memory_order_seq_cst)) && !(succ->marked.load(std::memory_order_seq_cst)) && pred->next[level]==succ;                
            }

            // Conditons are not met, release locks, abort and try again.
            if(!valid){
                for (auto const& x : locked_nodes){
                    x.first->unlock();
                }
                continue;
            }

            // All conditions satisfied, create the Node and insert it as we have all the required locks
            Node* new_node = new Node(key, value, top_level);

            // Update the predecessor and successors
            for (int level = 0; level <= top_level; level++){
                new_node->next[level] = succs[level];
            }

            for (int level = 0; level <= top_level; level++){
                preds[level]->next[level] = new_node;
            }

            // Mark the node as completely linked.
            new_node->fully_linked = true;
            
            // Release lock of all the nodes held once insert is complete
            for (auto const& x : locked_nodes){
                x.first->unlock();
            }
            
            return true;
        }catch(const std::exception& e){
            // If any exception occurs during the above insert, release locks of the held nodes and try again.
            std::cerr << e.what() << '\n';
            for (auto const& x : locked_nodes){
                    x.first->unlock();
            }
        }
    }
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
subq $0x250, %rsp            # imm = 0x250
movq %rdx, -0x1d8(%rbp)
movq %rdi, -0x10(%rbp)
movl %esi, -0x14(%rbp)
movq %rdx, -0x20(%rbp)
movq -0x10(%rbp), %rdi
movq %rdi, -0x1d0(%rbp)
callq 0xb0a0
movl %eax, -0x24(%rbp)
movl 0xc21e(%rip), %eax      # 0x17364
incl %eax
cltq
movq %rax, -0x1c8(%rbp)
leaq -0x41(%rbp), %rdi
movq %rdi, -0x1c0(%rbp)
callq 0xa2d0
movq -0x1c8(%rbp), %rsi
movq -0x1c0(%rbp), %rdx
leaq -0x40(%rbp), %rdi
callq 0xcaa0
jmp 0xb17a
leaq -0x41(%rbp), %rdi
callq 0xa510
movl 0xc1db(%rip), %eax      # 0x17364
incl %eax
cltq
movq %rax, -0x1e8(%rbp)
leaq -0x71(%rbp), %rdi
movq %rdi, -0x1e0(%rbp)
callq 0xa2d0
movq -0x1e8(%rbp), %rsi
movq -0x1e0(%rbp), %rdx
leaq -0x70(%rbp), %rdi
callq 0xcaa0
jmp 0xb1bd
leaq -0x71(%rbp), %rdi
callq 0xa510
movq $0x0, -0x80(%rbp)
movq -0x80(%rbp), %rax
movq %rax, -0x1f0(%rbp)
leaq -0x40(%rbp), %rdi
callq 0xa190
movq %rax, %rcx
movq -0x1f0(%rbp), %rax
cmpq %rcx, %rax
jae 0xb25b
movq -0x80(%rbp), %rsi
leaq -0x40(%rbp), %rdi
callq 0xa1b0
movq $0x0, (%rax)
movq -0x80(%rbp), %rsi
leaq -0x70(%rbp), %rdi
callq 0xa1b0
movq $0x0, (%rax)
movq -0x80(%rbp), %rax
addq $0x1, %rax
movq %rax, -0x80(%rbp)
jmp 0xb1ce
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x50(%rbp)
movl %eax, -0x54(%rbp)
leaq -0x41(%rbp), %rdi
callq 0xa510
jmp 0xb954
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x50(%rbp)
movl %eax, -0x54(%rbp)
leaq -0x71(%rbp), %rdi
callq 0xa510
jmp 0xb94b
jmp 0xb25d
jmp 0xb25f
movq -0x1d0(%rbp), %rdi
movl -0x14(%rbp), %esi
leaq -0x40(%rbp), %rdx
leaq -0x70(%rbp), %rcx
callq 0xaf90
movl %eax, -0x1f4(%rbp)
jmp 0xb27e
movl -0x1f4(%rbp), %eax
movl %eax, -0x84(%rbp)
cmpl $-0x1, -0x84(%rbp)
je 0xb306
movslq -0x84(%rbp), %rsi
leaq -0x70(%rbp), %rdi
callq 0xa1b0
movq (%rax), %rax
movq %rax, -0x90(%rbp)
movq -0x90(%rbp), %rdi
addq $0x68, %rdi
callq 0xcb10
testb $0x1, %al
jne 0xb301
jmp 0xb2c3
movq -0x90(%rbp), %rdi
addq $0x69, %rdi
callq 0xcb10
xorb $-0x1, %al
testb $0x1, %al
jne 0xb2db
jmp 0xb2ee
jmp 0xb2c3
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x50(%rbp)
movl %eax, -0x54(%rbp)
jmp 0xb942
movb $0x0, -0x1(%rbp)
movl $0x1, -0x94(%rbp)
jmp 0xb922
jmp 0xb25d
leaq -0xc8(%rbp), %rdi
callq 0xcba0
movb $0x1, -0xd9(%rbp)
movl $0x0, -0xe0(%rbp)
xorl %eax, %eax
testb $0x1, -0xd9(%rbp)
movb %al, -0x1f5(%rbp)
je 0xb346
movl -0xe0(%rbp), %eax
cmpl -0x24(%rbp), %eax
setle %al
movb %al, -0x1f5(%rbp)
movb -0x1f5(%rbp), %al
testb $0x1, %al
jne 0xb355
jmp 0xb507
movslq -0xe0(%rbp), %rsi
leaq -0x40(%rbp), %rdi
callq 0xa1b0
movq (%rax), %rax
movq %rax, -0xd0(%rbp)
movslq -0xe0(%rbp), %rsi
leaq -0x70(%rbp), %rdi
callq 0xa1b0
movq (%rax), %rax
movq %rax, -0xd8(%rbp)
leaq -0xc8(%rbp), %rdi
leaq -0xd0(%rbp), %rsi
callq 0xcbc0
movq %rax, -0x200(%rbp)
jmp 0xb3a5
movq -0x200(%rbp), %rax
cmpq $0x0, %rax
jne 0xb473
movq -0xd0(%rbp), %rdi
callq 0x9f50
jmp 0xb3c4
movl $0x1, -0xf4(%rbp)
leaq -0xd0(%rbp), %rdi
leaq -0xf4(%rbp), %rsi
callq 0xcc60
movl %edx, -0x20c(%rbp)
movq %rax, -0x208(%rbp)
jmp 0xb3f0
movl -0x20c(%rbp), %eax
movq -0x208(%rbp), %rcx
movq %rcx, -0x108(%rbp)
movl %eax, -0x100(%rbp)
movl -0x100(%rbp), %eax
movl %eax, -0xe8(%rbp)
movq -0x108(%rbp), %rax
movq %rax, -0xf0(%rbp)
leaq -0xc8(%rbp), %rdi
leaq -0xf0(%rbp), %rsi
callq 0xcc20
movb %dl, -0x219(%rbp)
movq %rax, -0x218(%rbp)
jmp 0xb446
movb -0x219(%rbp), %al
movq -0x218(%rbp), %rcx
movq %rcx, -0x118(%rbp)
movb %al, -0x110(%rbp)
jmp 0xb473
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x50(%rbp)
movl %eax, -0x54(%rbp)
jmp 0xb6c4
movq -0xd0(%rbp), %rdi
addq $0x68, %rdi
movl $0x5, %esi
callq 0xcc90
movb %al, %cl
xorl %eax, %eax
testb $0x1, %cl
movb %al, -0x21a(%rbp)
jne 0xb4e5
movq -0xd8(%rbp), %rdi
addq $0x68, %rdi
movl $0x5, %esi
callq 0xcc90
movb %al, %cl
xorl %eax, %eax
testb $0x1, %cl
movb %al, -0x21a(%rbp)
jne 0xb4e5
movq -0xd0(%rbp), %rdi
addq $0x28, %rdi
movslq -0xe0(%rbp), %rsi
callq 0xa1b0
movq (%rax), %rax
cmpq -0xd8(%rbp), %rax
sete %al
movb %al, -0x21a(%rbp)
movb -0x21a(%rbp), %al
andb $0x1, %al
movb %al, -0xd9(%rbp)
movl -0xe0(%rbp), %eax
addl $0x1, %eax
movl %eax, -0xe0(%rbp)
jmp 0xb323
testb $0x1, -0xd9(%rbp)
jne 0xb5a4
leaq -0xc8(%rbp), %rax
movq %rax, -0x120(%rbp)
movq -0x120(%rbp), %rdi
callq 0xcd20
movq %rax, -0x128(%rbp)
movq -0x120(%rbp), %rdi
callq 0xcd50
movq %rax, -0x130(%rbp)
leaq -0x128(%rbp), %rdi
leaq -0x130(%rbp), %rsi
callq 0xcd80
testb $0x1, %al
jne 0xb561
jmp 0xb595
leaq -0x128(%rbp), %rdi
callq 0xcdb0
movq %rax, -0x138(%rbp)
movq -0x138(%rbp), %rax
movq (%rax), %rdi
callq 0x9f70
jmp 0xb585
jmp 0xb587
leaq -0x128(%rbp), %rdi
callq 0xcdd0
jmp 0xb548
movl $0x5, -0x94(%rbp)
jmp 0xb8d0
movl $0x70, %edi
callq 0x3250
movq %rax, -0x228(%rbp)
jmp 0xb5b7
movq -0x1d8(%rbp), %rsi
movb $0x1, -0x161(%rbp)
movl -0x14(%rbp), %eax
movl %eax, -0x22c(%rbp)
leaq -0x160(%rbp), %rdi
callq 0x30f0
jmp 0xb5dc
movl -0x22c(%rbp), %esi
movq -0x228(%rbp), %rdi
movl -0x24(%rbp), %ecx
leaq -0x160(%rbp), %rdx
callq 0x9d30
jmp 0xb5fa
movb $0x0, -0x161(%rbp)
leaq -0x160(%rbp), %rdi
callq 0x3170
movq -0x228(%rbp), %rax
movq %rax, -0x140(%rbp)
movl $0x0, -0x168(%rbp)
movl -0x168(%rbp), %eax
cmpl -0x24(%rbp), %eax
jg 0xb7a3
movslq -0x168(%rbp), %rsi
leaq -0x70(%rbp), %rdi
callq 0xa1b0
movq (%rax), %rax
movq %rax, -0x238(%rbp)
movq -0x140(%rbp), %rdi
addq $0x28, %rdi
movslq -0x168(%rbp), %rsi
callq 0xa1b0
movq -0x238(%rbp), %rcx
movq %rcx, (%rax)
movl -0x168(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x168(%rbp)
jmp 0xb625
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x50(%rbp)
movl %eax, -0x54(%rbp)
jmp 0xb6a6
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x50(%rbp)
movl %eax, -0x54(%rbp)
leaq -0x160(%rbp), %rdi
callq 0x3170
testb $0x1, -0x161(%rbp)
jne 0xb6b1
jmp 0xb6c2
movq -0x228(%rbp), %rdi
movl $0x70, %esi
callq 0x3260
jmp 0xb6c4
movl -0x54(%rbp), %eax
movl $0x1, %ecx
cmpl %ecx, %eax
jne 0xb914
movq -0x50(%rbp), %rdi
callq 0x30c0
movq %rax, -0x198(%rbp)
movq -0x198(%rbp), %rdi
movq (%rdi), %rax
movq 0x10(%rax), %rax
callq *%rax
movq %rax, %rsi
movq 0xb8f2(%rip), %rdi      # 0x16ff0
callq 0x3240
movq %rax, -0x240(%rbp)
jmp 0xb70c
movq -0x240(%rbp), %rdi
movl $0xa, %esi
callq 0x32e0
jmp 0xb71f
leaq -0xc8(%rbp), %rax
movq %rax, -0x1a0(%rbp)
movq -0x1a0(%rbp), %rdi
callq 0xcd20
movq %rax, -0x1a8(%rbp)
movq -0x1a0(%rbp), %rdi
callq 0xcd50
movq %rax, -0x1b0(%rbp)
leaq -0x1a8(%rbp), %rdi
leaq -0x1b0(%rbp), %rsi
callq 0xcd80
testb $0x1, %al
jne 0xb76f
jmp 0xb8bd
leaq -0x1a8(%rbp), %rdi
callq 0xcdb0
movq %rax, -0x1b8(%rbp)
movq -0x1b8(%rbp), %rax
movq (%rax), %rdi
callq 0x9f70
jmp 0xb793
jmp 0xb795
leaq -0x1a8(%rbp), %rdi
callq 0xcdd0
jmp 0xb753
movl $0x0, -0x16c(%rbp)
movl -0x16c(%rbp), %eax
cmpl -0x24(%rbp), %eax
jg 0xb804
movq -0x140(%rbp), %rax
movq %rax, -0x248(%rbp)
movslq -0x16c(%rbp), %rsi
leaq -0x40(%rbp), %rdi
callq 0xa1b0
movq (%rax), %rdi
addq $0x28, %rdi
movslq -0x16c(%rbp), %rsi
callq 0xa1b0
movq -0x248(%rbp), %rcx
movq %rcx, (%rax)
movl -0x16c(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x16c(%rbp)
jmp 0xb7ad
movq -0x140(%rbp), %rdi
addq $0x69, %rdi
movl $0x1, %esi
callq 0xce00
leaq -0xc8(%rbp), %rax
movq %rax, -0x178(%rbp)
movq -0x178(%rbp), %rdi
callq 0xcd20
movq %rax, -0x180(%rbp)
movq -0x178(%rbp), %rdi
callq 0xcd50
movq %rax, -0x188(%rbp)
leaq -0x180(%rbp), %rdi
leaq -0x188(%rbp), %rsi
callq 0xcd80
testb $0x1, %al
jne 0xb866
jmp 0xb89a
leaq -0x180(%rbp), %rdi
callq 0xcdb0
movq %rax, -0x190(%rbp)
movq -0x190(%rbp), %rax
movq (%rax), %rdi
callq 0x9f70
jmp 0xb88a
jmp 0xb88c
leaq -0x180(%rbp), %rdi
callq 0xcdd0
jmp 0xb84d
movb $0x1, -0x1(%rbp)
movl $0x1, -0x94(%rbp)
jmp 0xb8d0
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x50(%rbp)
movl %eax, -0x54(%rbp)
callq 0x3380
jmp 0xb912
callq 0x3380
jmp 0xb8c4
jmp 0xb8c6
movl $0x0, -0x94(%rbp)
leaq -0xc8(%rbp), %rdi
callq 0xce30
movl -0x94(%rbp), %eax
movl %eax, -0x24c(%rbp)
testl %eax, %eax
je 0xb8ff
jmp 0xb8ee
movl -0x24c(%rbp), %eax
subl $0x5, %eax
je 0xb25d
jmp 0xb922
jmp 0xb25d
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x50(%rbp)
movl %eax, -0x54(%rbp)
jmp 0xb914
jmp 0xb914
leaq -0xc8(%rbp), %rdi
callq 0xce30
jmp 0xb942
leaq -0x70(%rbp), %rdi
callq 0xa1d0
leaq -0x40(%rbp), %rdi
callq 0xa1d0
movb -0x1(%rbp), %al
andb $0x1, %al
addq $0x250, %rsp            # imm = 0x250
popq %rbp
retq
leaq -0x70(%rbp), %rdi
callq 0xa1d0
leaq -0x40(%rbp), %rdi
callq 0xa1d0
movq -0x50(%rbp), %rdi
callq 0x33b0
movq %rax, %rdi
callq 0x61f0
nopw %cs:(%rax,%rax)