// File: /workspace/github_c_makefile_star2_O0/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
movq %rsp, %rbp
subq $0x40, %rsp
movq %rdi, -0x8(%rbp)
movl $0x0, -0x2c(%rbp)
movq -0x8(%rbp), %rax
movl 0x90(%rax), %eax
andl $0x8, %eax
cmpl $0x0, %eax
jne 0x62c5a
movq -0x8(%rbp), %rdi
leaq 0x82518(%rip), %rsi     # 0xe5168
callq 0x35d40
jmp 0x62ed6
movq -0x8(%rbp), %rax
movl 0x90(%rax), %eax
andl $0x1020, %eax           # imm = 0x1020
cmpl $0x0, %eax
je 0x62cc8
movq -0x8(%rbp), %rax
movq %rax, -0x38(%rbp)
movq -0x8(%rbp), %rax
movl 0x90(%rax), %eax
andl $0x1000, %eax           # imm = 0x1000
cmpl $0x0, %eax
je 0x62c9e
leaq 0xb14bf(%rip), %rax     # 0x114150
movq 0xc8(%rax), %rax
movq %rax, -0x40(%rbp)
jmp 0x62cad
leaq 0xb14ab(%rip), %rax     # 0x114150
movq 0x58(%rax), %rax
movq %rax, -0x40(%rbp)
movq -0x38(%rbp), %rdi
movq -0x40(%rbp), %rsi
callq 0x35a70
movq -0x8(%rbp), %rdi
callq 0x62940
jmp 0x62e7f
movq -0x8(%rbp), %rdi
callq 0x62990
movq -0x8(%rbp), %rax
movq 0x38(%rax), %rax
movq %rax, -0x18(%rbp)
movq -0x8(%rbp), %rax
movl 0x30(%rax), %eax
movl %eax, -0x1c(%rbp)
movq -0x8(%rbp), %rax
movq 0x40(%rax), %rax
movq %rax, -0x28(%rbp)
movq -0x8(%rbp), %rdi
movq -0x8(%rbp), %rax
movslq 0x108(%rax), %rsi
callq 0x364f0
movl $0x0, -0xc(%rbp)
movl -0xc(%rbp), %eax
movq -0x8(%rbp), %rcx
cmpl 0x108(%rcx), %eax
jge 0x62e2c
movq -0x8(%rbp), %rax
movq 0x100(%rax), %rax
movslq -0xc(%rbp), %rcx
imulq $0x18, %rcx, %rcx
addq %rcx, %rax
movl 0x8(%rax), %ecx
movq -0x8(%rbp), %rax
movl %ecx, 0x30(%rax)
movq -0x8(%rbp), %rax
movq 0x100(%rax), %rax
movslq -0xc(%rbp), %rcx
imulq $0x18, %rcx, %rcx
addq %rcx, %rax
movq (%rax), %rcx
movq -0x8(%rbp), %rax
movq %rcx, 0x38(%rax)
movq -0x8(%rbp), %rax
movq 0x100(%rax), %rax
movslq -0xc(%rbp), %rcx
imulq $0x18, %rcx, %rcx
addq %rcx, %rax
movq 0x10(%rax), %rcx
movq -0x8(%rbp), %rax
movq %rcx, 0x40(%rax)
cmpl $0x0, -0x2c(%rbp)
jne 0x62dad
movq -0x8(%rbp), %rax
movq 0x40(%rax), %rax
movl 0x20(%rax), %eax
andl $0x2, %eax
cmpl $0x0, %eax
jne 0x62dad
movq -0x8(%rbp), %rdi
callq 0x62bc0
movl $0x1, -0x2c(%rbp)
movq -0x8(%rbp), %rdi
movl $0x7, %esi
callq 0x288a0
movq -0x8(%rbp), %rax
movl 0x30(%rax), %ecx
movq -0x8(%rbp), %rax
movq 0x100(%rax), %rax
movslq -0xc(%rbp), %rdx
imulq $0x18, %rdx, %rdx
addq %rdx, %rax
movl %ecx, 0x8(%rax)
movq -0x8(%rbp), %rax
movq 0x38(%rax), %rcx
movq -0x8(%rbp), %rax
movq 0x100(%rax), %rax
movslq -0xc(%rbp), %rdx
imulq $0x18, %rdx, %rdx
addq %rdx, %rax
movq %rcx, (%rax)
movq -0x8(%rbp), %rax
movq 0x40(%rax), %rcx
movq -0x8(%rbp), %rax
movq 0x100(%rax), %rax
movslq -0xc(%rbp), %rdx
imulq $0x18, %rdx, %rdx
addq %rdx, %rax
movq %rcx, 0x10(%rax)
movl -0xc(%rbp), %eax
addl $0x1, %eax
movl %eax, -0xc(%rbp)
jmp 0x62d0e
movq -0x18(%rbp), %rcx
movq -0x8(%rbp), %rax
movq %rcx, 0x38(%rax)
movl -0x1c(%rbp), %ecx
movq -0x8(%rbp), %rax
movl %ecx, 0x30(%rax)
movq -0x28(%rbp), %rcx
movq -0x8(%rbp), %rax
movq %rcx, 0x40(%rax)
movq -0x8(%rbp), %rdi
callq 0x62940
cmpl $0x0, -0x2c(%rbp)
je 0x62e7d
leaq 0xb0a94(%rip), %rax     # 0x1138f8
movq 0x560(%rax), %rcx
addq $0x1, %rcx
leaq 0xb0a82(%rip), %rax     # 0x1138f8
movq %rcx, 0x560(%rax)
jmp 0x62e7f
leaq 0xb0a72(%rip), %rax     # 0x1138f8
movq 0x1c8(%rax), %rax
cmpq $0x0, 0x28(%rax)
je 0x62ed6
leaq 0xb0a5d(%rip), %rax     # 0x1138f8
cmpl $0x0, 0x2f0(%rax)
jne 0x62ed6
movq -0x8(%rbp), %rdi
leaq 0xb0a49(%rip), %rax     # 0x1138f8
movq 0x1c8(%rax), %rsi
movq -0x8(%rbp), %rax
movq 0x8(%rax), %rax
movl 0x30(%rax), %edx
movq -0x8(%rbp), %rax
movq 0x38(%rax), %rcx
movq -0x8(%rbp), %rax
movl 0x30(%rax), %r8d
callq 0x3f4b0
addq $0x40, %rsp
popq %rbp
retq
nopl (%rax)