// File: /workspace/github_c_makefile_star2_O3/nilyang[P]redis-tdd-annotation/src/multi.c
// Function: execCommand
void execCommand(redisClient *c) {
    int j;
    robj **orig_argv;
    int orig_argc;
    struct redisCommand *orig_cmd;
    int must_propagate = 0; /* Need to propagate MULTI/EXEC to AOF / slaves? */

    // 客户端没有执行事务
    if (!(c->flags & REDIS_MULTI)) {
        addReplyError(c,"EXEC without MULTI");
        return;
    }

    /* Check if we need to abort the EXEC because:
     *
     * 检查是否需要阻止事务执行，因为：
     *
     * 1) Some WATCHed key was touched.
     *    有被监视的键已经被修改了
     *
     * 2) There was a previous error while queueing commands.
     *    命令在入队时发生错误
     *    （注意这个行为是 2.6.4 以后才修改的，之前是静默处理入队出错命令）
     *
     * A failed EXEC in the first case returns a multi bulk nil object
     * (technically it is not an error but a special behavior), while
     * in the second an EXECABORT error is returned. 
     *
     * 第一种情况返回多个批量回复的空对象
     * 而第二种情况则返回一个 EXECABORT 错误
     */
    if (c->flags & (REDIS_DIRTY_CAS|REDIS_DIRTY_EXEC)) {

        addReply(c, c->flags & REDIS_DIRTY_EXEC ? shared.execaborterr :
                                                  shared.nullmultibulk);

        // 取消事务
        discardTransaction(c);

        goto handle_monitor;
    }

    /* Exec all the queued commands */
    // 已经可以保证安全性了，取消客户端对所有键的监视
    unwatchAllKeys(c); /* Unwatch ASAP otherwise we'll waste CPU cycles */

    // 因为事务中的命令在执行时可能会修改命令和命令的参数
    // 所以为了正确地传播命令，需要现备份这些命令和参数
    orig_argv = c->argv;
    orig_argc = c->argc;
    orig_cmd = c->cmd;

    addReplyMultiBulkLen(c,c->mstate.count);

    // 执行事务中的命令
    for (j = 0; j < c->mstate.count; j++) {

        // 因为 Redis 的命令必须在客户端的上下文中执行
        // 所以要将事务队列中的命令、命令参数等设置给客户端
        c->argc = c->mstate.commands[j].argc;
        c->argv = c->mstate.commands[j].argv;
        c->cmd = c->mstate.commands[j].cmd;

        /* Propagate a MULTI request once we encounter the first write op.
         *
         * 当遇上第一个写命令时，传播 MULTI 命令。
         *
         * This way we'll deliver the MULTI/..../EXEC block as a whole and
         * both the AOF and the replication link will have the same consistency
         * and atomicity guarantees. 
         *
         * 这可以确保服务器和 AOF 文件以及附属节点的数据一致性。
         */
        if (!must_propagate && !(c->cmd->flags & REDIS_CMD_READONLY)) {

            // 传播 MULTI 命令
            execCommandPropagateMulti(c);

            // 计数器，只发送一次
            must_propagate = 1;
        }

        // 执行命令
        call(c,REDIS_CALL_FULL);

        /* Commands may alter argc/argv, restore mstate. */
        // 因为执行后命令、命令参数可能会被改变
        // 比如 SPOP 会被改写为 SREM
        // 所以这里需要更新事务队列中的命令和参数
        // 确保附属节点和 AOF 的数据一致性
        c->mstate.commands[j].argc = c->argc;
        c->mstate.commands[j].argv = c->argv;
        c->mstate.commands[j].cmd = c->cmd;
    }

    // 还原命令、命令参数
    c->argv = orig_argv;
    c->argc = orig_argc;
    c->cmd = orig_cmd;

    // 清理事务状态
    discardTransaction(c);

    /* Make sure the EXEC command will be propagated as well if MULTI
     * was already propagated. */
    // 将服务器设为脏，确保 EXEC 命令也会被传播
    if (must_propagate) server.dirty++;

handle_monitor:
    /* Send EXEC to clients waiting data from MONITOR. We do it here
     * since the natural order of commands execution is actually:
     * MUTLI, EXEC, ... commands inside transaction ...
     * Instead EXEC is flagged as REDIS_CMD_SKIP_MONITOR in the command
     * table, and we do it here with correct ordering. */
    if (listLength(server.monitors) && !server.loading)
        replicationFeedMonitors(c,server.monitors,c->db->id,c->argv,c->argc);
}

// Assembly:
pushq %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0x18, %rsp
movq %rdi, %rbx
movl 0x90(%rdi), %eax
testb $0x8, %al
jne 0x4bf5f
leaq 0x4f19f(%rip), %rsi     # 0x9b0e8
movq %rbx, %rdi
addq $0x18, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
jmp 0x2dc63
testl $0x1020, %eax           # imm = 0x1020
je 0x4c010
btl $0xc, %eax
leaq 0x7e88b(%rip), %rax     # 0xca800
movl $0x58, %ecx
movl $0xc8, %edx
cmovaeq %rcx, %rdx
movq (%rax,%rdx), %rsi
movq %rbx, %rdi
callq 0x2d97c
movq %rbx, %rdi
callq 0x4bbee
movq $0x0, 0x100(%rbx)
movl $0x0, 0x108(%rbx)
andl $0xffffefd7, 0x90(%rbx) # imm = 0xFFFFEFD7
movq %rbx, %rdi
callq 0x4bd35
leaq 0x7dfe3(%rip), %rax     # 0xc9fa8
movq 0x1c8(%rax), %rsi
cmpq $0x0, 0x28(%rsi)
je 0x4c001
cmpl $0x0, 0x2f0(%rax)
jne 0x4c001
movq 0x8(%rbx), %rax
movl 0x30(%rax), %edx
movq 0x38(%rbx), %rcx
movl 0x30(%rbx), %r8d
movq %rbx, %rdi
addq $0x18, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
jmp 0x339e1
addq $0x18, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
movq %rbx, %rdi
callq 0x4bd35
movq 0x38(%rbx), %r15
movl 0x30(%rbx), %ebp
movq 0x40(%rbx), %r14
movslq 0x108(%rbx), %rsi
movq %rbx, %rdi
callq 0x2e168
cmpl $0x0, 0x108(%rbx)
jle 0x4c133
movl %ebp, 0xc(%rsp)
movq %r14, 0x10(%rsp)
movq 0x100(%rbx), %rax
xorl %r12d, %r12d
movl $0x10, %r13d
xorl %ebp, %ebp
movl %ebp, %ecx
movl -0x8(%rax,%r13), %edx
movl %edx, 0x30(%rbx)
movq -0x10(%rax,%r13), %rdx
movq %rdx, 0x38(%rbx)
movq (%rax,%r13), %rax
movq %rax, 0x40(%rbx)
xorl %ebp, %ebp
testl %ecx, %ecx
jne 0x4c08c
movb $0x1, %r14b
testb $0x2, 0x20(%rax)
jne 0x4c094
movq %rbx, %rdi
callq 0x4bed1
xorl %r14d, %r14d
movl $0x1, %ebp
movq %rbx, %rdi
movl $0x7, %esi
callq 0x25d39
movl 0x30(%rbx), %ecx
movq 0x100(%rbx), %rax
movl %ecx, -0x8(%rax,%r13)
movq 0x38(%rbx), %rcx
movq %rcx, -0x10(%rax,%r13)
movq 0x40(%rbx), %rcx
movq %rcx, (%rax,%r13)
incq %r12
movslq 0x108(%rbx), %rcx
addq $0x18, %r13
cmpq %rcx, %r12
jl 0x4c05a
movq %r15, 0x38(%rbx)
movl 0xc(%rsp), %eax
movl %eax, 0x30(%rbx)
movq 0x10(%rsp), %rax
movq %rax, 0x40(%rbx)
movq %rbx, %rdi
callq 0x4bbee
movq $0x0, 0x100(%rbx)
movl $0x0, 0x108(%rbx)
andl $0xffffefd7, 0x90(%rbx) # imm = 0xFFFFEFD7
movq %rbx, %rdi
callq 0x4bd35
testb %r14b, %r14b
jne 0x4bfbe
leaq 0x7de81(%rip), %rax     # 0xc9fa8
incq 0x560(%rax)
jmp 0x4bfbe
movq %r15, 0x38(%rbx)
movl %ebp, 0x30(%rbx)
movq %r14, 0x40(%rbx)
jmp 0x4bf8f