// File: /workspace/github_c_makefile_star2_O2/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 $0x28, %rsp
movq %rdi, %rbx
movl 0x90(%rdi), %eax
testb $0x8, %al
jne 0x486a7
leaq 0x48a17(%rip), %rsi     # 0x910a8
movq %rbx, %rdi
addq $0x28, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
jmp 0x2c2da
testw $0x1020, %ax            # imm = 0x1020
je 0x486dd
btl $0xc, %eax
leaq 0x7f148(%rip), %rax     # 0xc7800
pushq $0x58
popq %rcx
movl $0xc8, %edx
cmovaeq %rcx, %rdx
movq (%rax,%rdx), %rsi
movq %rbx, %rdi
callq 0x2c10d
movq %rbx, %rdi
callq 0x4849b
jmp 0x487bb
movq %rbx, %rdi
callq 0x484c6
movl 0x30(%rbx), %eax
movl %eax, 0xc(%rsp)
movups 0x38(%rbx), %xmm0
movaps %xmm0, 0x10(%rsp)
movslq 0x108(%rbx), %rsi
movq %rbx, %rdi
callq 0x2c7c7
xorl %r15d, %r15d
pushq $0x10
popq %r12
pushq $0x1
popq %r14
pushq $0x7
popq %rbp
xorl %r13d, %r13d
movslq 0x108(%rbx), %rax
cmpq %rax, %r15
jge 0x48790
movq 0x100(%rbx), %rax
movl -0x8(%rax,%r12), %ecx
movl %ecx, 0x30(%rbx)
movq -0x10(%rax,%r12), %rcx
movq %rcx, 0x38(%rbx)
movq (%rax,%r12), %rax
movq %rax, 0x40(%rbx)
testl %r13d, %r13d
movl %r14d, %r13d
jne 0x4875d
xorl %r13d, %r13d
testb $0x2, 0x20(%rax)
jne 0x4875d
movq %rbx, %rdi
callq 0x4861f
movl %r14d, %r13d
movq %rbx, %rdi
movl %ebp, %esi
callq 0x24c94
movl 0x30(%rbx), %eax
movq 0x100(%rbx), %rcx
movl %eax, -0x8(%rcx,%r12)
movq 0x38(%rbx), %rax
movq %rax, -0x10(%rcx,%r12)
movq 0x40(%rbx), %rax
movq %rax, (%rcx,%r12)
incq %r15
addq $0x18, %r12
jmp 0x48715
movl 0xc(%rsp), %eax
movl %eax, 0x30(%rbx)
movaps 0x10(%rsp), %xmm0
movups %xmm0, 0x38(%rbx)
movq %rbx, %rdi
callq 0x4849b
testl %r13d, %r13d
je 0x487bb
leaq 0x7e7f4(%rip), %rax     # 0xc6fa8
incq 0x560(%rax)
leaq 0x7e7e6(%rip), %rax     # 0xc6fa8
movq 0x1c8(%rax), %rsi
cmpq $0x0, 0x28(%rsi)
je 0x487fe
cmpl $0x0, 0x2f0(%rax)
jne 0x487fe
movq 0x8(%rbx), %rax
movl 0x30(%rax), %edx
movq 0x38(%rbx), %rcx
movl 0x30(%rbx), %r8d
movq %rbx, %rdi
addq $0x28, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
jmp 0x3186b
addq $0x28, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq