// File: cybozu[P]yrmcds/src/memcache/gc.cpp
// Function: yrmcds::memcache::gc_thread::gc()
void gc_thread::gc() {
    std::time_t t = g_stats.flush_time.load();
    bool flush = (t != 0) && (std::time(nullptr) >= t);
    unsigned int evict_age = 0;
    if( (! flush) &&
        (g_stats.used_memory.load(std::memory_order_relaxed) >
         g_config.memory_limit()) ) {
        unsigned int oldest_age =
            g_stats.oldest_age.load(std::memory_order_relaxed);
        unsigned int one_hour = 3600U / g_config.gc_interval() + 1;
        if( oldest_age < (one_hour * 2) ) {
            evict_age = std::max(1U, oldest_age / 2);
        } else {
            evict_age = oldest_age - one_hour;
        }
        cybozu::logger::warning() << "Evicting object of "
                                  << evict_age << " gc old";
    }

    auto pred =
        [this,flush,evict_age](const cybozu::hash_key& k, object& obj) ->bool {
        if( flush && (! obj.locked()) ) {
            if( ! m_slaves.empty() )
                repl_delete(m_slaves, k);
            return true;
        }
        if( evict_age > 0 && obj.age() >= evict_age && (! obj.locked()) ) {
            ++ m_last_evictions;
            if( ! m_slaves.empty() )
                repl_delete(m_slaves, k);
            return true;
        }
        if( obj.expired() ) {
            ++ m_last_expirations;
            if( ! m_slaves.empty() )
                repl_delete(m_slaves, k);
            return true;
        }

        obj.survive(m_flushers);
        if( ++m_objects_in_bucket == 2 )
            ++ m_conflicts;
        ++ m_objects;
        std::size_t size = obj.size();
        if( size < 1024 ) {
            ++ m_objects_under_1k;
        } else if( size < 4096 ) {
            ++ m_objects_under_4k;
        } else if( size < (16 <<10) ) {
            ++ m_objects_under_16k;
        } else if( size < (64 <<10) ) {
            ++ m_objects_under_64k;
        } else if( size < (256 <<10) ) {
            ++ m_objects_under_256k;
        } else if( size < (1 <<20) ) {
            ++ m_objects_under_1m;
        } else if( size < (4 <<20) ) {
            ++ m_objects_under_4m;
        } else {
            ++ m_objects_huge;
        }
        m_used_memory += sizeof(cybozu::hash_key) + k.length() + sizeof(object);
        if( g_config.heap_data_limit() >= obj.size() )
            m_used_memory += obj.size();
        m_oldest_age = std::max(m_oldest_age, obj.age());
        m_largest_object_size = std::max(m_largest_object_size, obj.size());
        if( ! m_new_slaves.empty() )
            repl_object(m_new_slaves, k, obj, false);
        return false;
    };

    // Putting the thread to sleep tens of microseconds with each loop is desired, but due to the precision of the timer,
    // it's not appropriate to sleep with each loop. The `initial_repl_sleep_delay_usec` is accumulated until it
    // exceeds 10000 microseconds (10 milliseconds), and then the thread is put to sleep all at once when this limit is exceeded.
    constexpr std::uint64_t SLEEP_THRESHOLD = 10000;
    std::uint64_t sleep_sum = 0;

    for( auto it = m_hash.begin(); it != m_hash.end(); ++it ) {
        m_objects_in_bucket = 0;
        it->gc(pred);
        m_flushers.clear();

        if( ! m_new_slaves.empty() ) {
            sleep_sum += g_config.initial_repl_sleep_delay_usec();
            if( sleep_sum >= SLEEP_THRESHOLD ) {
                std::this_thread::sleep_for(
                    std::chrono::microseconds(sleep_sum));
                sleep_sum = 0;
            }
        }
    }

    if( flush )
        g_stats.flush_time.store(0);
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
subq $0x190, %rsp            # imm = 0x190
movq %rdi, -0x70(%rbp)
movq -0x70(%rbp), %rax
movq %rax, -0x120(%rbp)
leaq 0x4f0ef(%rip), %rax     # 0xb0000
addq $0xc0, %rax
movq %rax, -0x58(%rbp)
movl $0x5, -0x5c(%rbp)
movq -0x58(%rbp), %rax
movq %rax, -0x118(%rbp)
movl -0x5c(%rbp), %edi
movl $0xffff, %esi           # imm = 0xFFFF
callq 0x104e0
movl %eax, -0x10c(%rbp)
jmp 0x60f42
movl -0x10c(%rbp), %eax
movl %eax, -0x60(%rbp)
movl -0x5c(%rbp), %eax
movl %eax, -0x124(%rbp)
decl %eax
subl $0x2, %eax
jb 0x60f7a
jmp 0x60f5d
movl -0x124(%rbp), %eax
subl $0x5, %eax
je 0x60f8a
jmp 0x60f6a
movq -0x118(%rbp), %rax
movq (%rax), %rax
movq %rax, -0x68(%rbp)
jmp 0x60fa2
movq -0x118(%rbp), %rax
movq (%rax), %rax
movq %rax, -0x68(%rbp)
jmp 0x60fa2
movq -0x118(%rbp), %rax
movq (%rax), %rax
movq %rax, -0x68(%rbp)
jmp 0x60fa2
movq %rax, %rdi
callq 0xa7e0
movq -0x68(%rbp), %rax
movq %rax, -0x78(%rbp)
xorl %eax, %eax
cmpq $0x0, -0x78(%rbp)
movb %al, -0x125(%rbp)
je 0x60fcf
xorl %eax, %eax
movl %eax, %edi
callq 0x85f0
cmpq -0x78(%rbp), %rax
setge %al
movb %al, -0x125(%rbp)
movb -0x125(%rbp), %al
andb $0x1, %al
movb %al, -0x79(%rbp)
movl $0x0, -0x80(%rbp)
testb $0x1, -0x79(%rbp)
jne 0x61215
leaq 0x4f00e(%rip), %rax     # 0xb0000
addq $0x28, %rax
movq %rax, -0x40(%rbp)
movl $0x0, -0x44(%rbp)
movq -0x40(%rbp), %rax
movq %rax, -0x138(%rbp)
movl -0x44(%rbp), %edi
movl $0xffff, %esi           # imm = 0xFFFF
callq 0x104e0
movl %eax, -0x48(%rbp)
movl -0x44(%rbp), %eax
movl %eax, -0x12c(%rbp)
decl %eax
subl $0x2, %eax
jb 0x6104b
jmp 0x6102e
movl -0x12c(%rbp), %eax
subl $0x5, %eax
je 0x6105b
jmp 0x6103b
movq -0x138(%rbp), %rax
movq (%rax), %rax
movq %rax, -0x50(%rbp)
jmp 0x61069
movq -0x138(%rbp), %rax
movq (%rax), %rax
movq %rax, -0x50(%rbp)
jmp 0x61069
movq -0x138(%rbp), %rax
movq (%rax), %rax
movq %rax, -0x50(%rbp)
movq -0x50(%rbp), %rax
movq %rax, -0x140(%rbp)
leaq 0x4ec85(%rip), %rdi     # 0xafd00
callq 0x39c60
movq %rax, %rcx
movq -0x140(%rbp), %rax
cmpq %rcx, %rax
jbe 0x61215
leaq 0x4ef66(%rip), %rax     # 0xb0000
addq $0x38, %rax
movq %rax, -0x28(%rbp)
movl $0x0, -0x2c(%rbp)
movq -0x28(%rbp), %rax
movq %rax, -0x150(%rbp)
movl -0x2c(%rbp), %edi
movl $0xffff, %esi           # imm = 0xFFFF
callq 0x104e0
movl %eax, -0x30(%rbp)
movl -0x2c(%rbp), %eax
movl %eax, -0x144(%rbp)
decl %eax
subl $0x2, %eax
jb 0x610f1
jmp 0x610d6
movl -0x144(%rbp), %eax
subl $0x5, %eax
je 0x610ff
jmp 0x610e3
movq -0x150(%rbp), %rax
movl (%rax), %eax
movl %eax, -0x34(%rbp)
jmp 0x6110b
movq -0x150(%rbp), %rax
movl (%rax), %eax
movl %eax, -0x34(%rbp)
jmp 0x6110b
movq -0x150(%rbp), %rax
movl (%rax), %eax
movl %eax, -0x34(%rbp)
movl -0x34(%rbp), %eax
movl %eax, -0x84(%rbp)
leaq 0x4ebe5(%rip), %rdi     # 0xafd00
callq 0x39cc0
movl %eax, %ecx
movl $0xe10, %eax            # imm = 0xE10
xorl %edx, %edx
divl %ecx
addl $0x1, %eax
movl %eax, -0x88(%rbp)
movl -0x84(%rbp), %eax
movl -0x88(%rbp), %ecx
shll %ecx
cmpl %ecx, %eax
jae 0x61178
movl $0x1, -0x8c(%rbp)
movl -0x84(%rbp), %eax
shrl %eax
movl %eax, -0x90(%rbp)
leaq -0x8c(%rbp), %rdi
leaq -0x90(%rbp), %rsi
callq 0x35310
movl (%rax), %eax
movl %eax, -0x80(%rbp)
jmp 0x61187
movl -0x84(%rbp), %eax
subl -0x88(%rbp), %eax
movl %eax, -0x80(%rbp)
leaq -0xa0(%rbp), %rdi
movq %rdi, -0x160(%rbp)
callq 0x1b6e0
movq -0x160(%rbp), %rdi
leaq 0x1bee0(%rip), %rsi     # 0x7d088
callq 0x61d10
movq %rax, -0x158(%rbp)
jmp 0x611b6
movq -0x158(%rbp), %rdi
leaq -0x80(%rbp), %rsi
callq 0x24620
movq %rax, -0x168(%rbp)
jmp 0x611cf
movq -0x168(%rbp), %rdi
leaq 0x1bebf(%rip), %rsi     # 0x7d09c
callq 0x22a40
jmp 0x611e4
leaq -0xa0(%rbp), %rdi
callq 0xa770
jmp 0x61215
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0xa8(%rbp)
movl %eax, -0xac(%rbp)
leaq -0xa0(%rbp), %rdi
callq 0xa770
jmp 0x61445
movq -0x120(%rbp), %rax
movq %rax, -0xc0(%rbp)
movb -0x79(%rbp), %cl
andb $0x1, %cl
movb %cl, -0xb8(%rbp)
movl -0x80(%rbp), %ecx
movl %ecx, -0xb4(%rbp)
movq $0x2710, -0xc8(%rbp) # imm = 0x2710
movq $0x0, -0xd0(%rbp)
movq 0x10(%rax), %rdi
callq 0x3a600
movq %rax, -0xd8(%rbp)
movq -0x120(%rbp), %rax
movq 0x10(%rax), %rdi
callq 0x3a630
movq %rax, -0xe0(%rbp)
leaq -0xd8(%rbp), %rdi
leaq -0xe0(%rbp), %rsi
callq 0x3a660
testb $0x1, %al
jne 0x61291
jmp 0x613a2
movq -0x120(%rbp), %rax
movl $0x0, 0x90(%rax)
leaq -0xd8(%rbp), %rdi
callq 0x61d50
movq %rax, -0x178(%rbp)
leaq -0x100(%rbp), %rdi
movq %rdi, -0x170(%rbp)
leaq -0xc0(%rbp), %rsi
callq 0x61460
movq -0x178(%rbp), %rdi
movq -0x170(%rbp), %rsi
callq 0x61d70
jmp 0x612e4
leaq -0x100(%rbp), %rdi
callq 0x508e0
movq -0x120(%rbp), %rdi
addq $0x98, %rdi
callq 0x61e60
movq -0x120(%rbp), %rdi
addq $0x30, %rdi
callq 0x39c80
testb $0x1, %al
jne 0x6138f
leaq 0x4e9e2(%rip), %rdi     # 0xafd00
callq 0x61e80
addq -0xd0(%rbp), %rax
movq %rax, -0xd0(%rbp)
cmpq $0x2710, -0xd0(%rbp) # imm = 0x2710
jb 0x6138d
leaq -0x108(%rbp), %rdi
leaq -0xd0(%rbp), %rsi
callq 0x61f50
leaq -0x108(%rbp), %rdi
callq 0x61ea0
movq $0x0, -0xd0(%rbp)
jmp 0x6138d
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0xa8(%rbp)
movl %eax, -0xac(%rbp)
leaq -0x100(%rbp), %rdi
callq 0x508e0
jmp 0x61445
jmp 0x6138f
jmp 0x61391
leaq -0xd8(%rbp), %rdi
callq 0x3a730
jmp 0x6125e
testb $0x1, -0x79(%rbp)
je 0x6143c
leaq 0x4ec4d(%rip), %rax     # 0xb0000
addq $0xc0, %rax
movq %rax, -0x8(%rbp)
movq $0x0, -0x10(%rbp)
movl $0x5, -0x14(%rbp)
movq -0x8(%rbp), %rax
movq %rax, -0x188(%rbp)
movl -0x14(%rbp), %edi
movl $0xffff, %esi           # imm = 0xFFFF
callq 0x104e0
movl %eax, -0x18(%rbp)
movl -0x14(%rbp), %eax
movl %eax, -0x17c(%rbp)
movq -0x10(%rbp), %rcx
movq %rcx, -0x20(%rbp)
subl $0x3, %eax
je 0x6141c
jmp 0x613ff
movl -0x17c(%rbp), %eax
subl $0x5, %eax
je 0x6142c
jmp 0x6140c
movq -0x188(%rbp), %rax
movq -0x20(%rbp), %rcx
movq %rcx, (%rax)
jmp 0x6143a
movq -0x188(%rbp), %rax
movq -0x20(%rbp), %rcx
movq %rcx, (%rax)
jmp 0x6143a
movq -0x188(%rbp), %rcx
movq -0x20(%rbp), %rax
xchgq %rax, (%rcx)
jmp 0x6143c
addq $0x190, %rsp            # imm = 0x190
popq %rbp
retq
movq -0xa8(%rbp), %rdi
callq 0x8bc0
nopw %cs:(%rax,%rax)