// File: /workspace/github_cpp_makefile_star2_O2/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
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0x38, %rsp
movq %rdi, %rbx
leaq 0x1d43c(%rip), %r15     # 0x3bec0
movq 0xc0(%r15), %r14
testq %r14, %r14
je 0x1eab4
movq $0x0, 0x8(%rsp)
xorl %edi, %edi
callq 0x74c0
andl $0x0, 0x4(%rsp)
movb $0x1, %r13b
movb $0x1, %cl
cmpq %r14, %rax
jl 0x1eab9
jmp 0x1eb5f
andl $0x0, 0x4(%rsp)
movq 0x28(%r15), %rax
leaq 0x1d0fc(%rip), %rsi     # 0x3bbc0
movq $0x0, 0x8(%rsp)
movl $0x0, %r13d
movl $0x0, %ecx
cmpq 0xe8(%rsi), %rax
jbe 0x1eb5f
movl 0x38(%r15), %ecx
movl $0xe10, %eax            # imm = 0xE10
xorl %edx, %edx
divl 0x108(%rsi)
leal 0x2(,%rax,2), %edx
cmpl %edx, %ecx
jae 0x1eb07
shrl %ecx
cmpl $0x1, %ecx
adcl $0x0, %ecx
jmp 0x1eb0b
incl %eax
subl %eax, %ecx
movl %ecx, 0x4(%rsp)
leaq 0x18(%rsp), %r14
movq %r14, %rdi
callq 0xd0b4
leaq 0xa066(%rip), %rsi      # 0x28b89
movq %r14, %rdi
callq 0x1ee96
leaq 0x4(%rsp), %rsi
movq %rax, %rdi
callq 0xf382
leaq 0xa05e(%rip), %rsi      # 0x28b9d
movq %rax, %rdi
callq 0xef00
leaq 0x18(%rsp), %rdi
callq 0x86e4
xorl %r13d, %r13d
movl 0x4(%rsp), %eax
movq %rax, 0x8(%rsp)
xorl %ecx, %ecx
movl %ecx, 0x14(%rsp)
movq 0x10(%rbx), %rax
movq 0x8(%rax), %r14
leaq 0x98(%rbx), %r15
xorl %ebp, %ebp
leaq 0x18(%rsp), %r12
cmpq 0x10(%rax), %r14
je 0x1ec13
andl $0x0, 0x90(%rbx)
andq $0x0, 0x20(%rsp)
movq %rbx, 0x18(%rsp)
movb %r13b, 0x20(%rsp)
movq 0x8(%rsp), %rax
movl %eax, 0x24(%rsp)
leaq 0xb4(%rip), %rax        # 0x1ec5e
movq %rax, 0x30(%rsp)
leaq 0x258(%rip), %rax       # 0x1ee0e
movq %rax, 0x28(%rsp)
movq %r14, %rdi
movq %r12, %rsi
callq 0x1eeae
movq %r12, %rdi
callq 0x8d90
movq %r15, %rdi
callq 0x1ef36
movq 0x30(%rbx), %rax
cmpq 0x38(%rbx), %rax
je 0x1ec06
leaq 0x1cfd9(%rip), %rax     # 0x3bbc0
addq 0xf8(%rax), %rbp
cmpq $0x2710, %rbp           # imm = 0x2710
jb 0x1ec06
movq %rbp, 0x18(%rsp)
movq %r12, %rdi
callq 0x1ef3e
xorl %ebp, %ebp
addq $0x30, %r14
movq 0x10(%rbx), %rax
jmp 0x1eb79
cmpb $0x0, 0x14(%rsp)
je 0x1ec2a
xorl %eax, %eax
leaq 0x1d29d(%rip), %rcx     # 0x3bec0
xchgq %rax, 0xc0(%rcx)
addq $0x38, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
movq %rax, %rbx
leaq 0x18(%rsp), %rdi
callq 0x86e4
jmp 0x1ec55
movq %rax, %rbx
leaq 0x18(%rsp), %rdi
callq 0x8d90
movq %rbx, %rdi
callq 0x7990
nop