// File: /workspace/github_cpp_makefile_star2_O1/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 0x232be(%rip), %r15     # 0x46ec0
movq 0xc0(%r15), %r14
movl $0x0, 0xc(%rsp)
testq %r14, %r14
je 0x23c26
xorl %edi, %edi
callq 0x74a0
cmpq %r14, %rax
setge %r13b
jmp 0x23c29
xorl %r13d, %r13d
testb %r13b, %r13b
jne 0x23cf8
movq 0x28(%r15), %rax
leaq 0x22f83(%rip), %rcx     # 0x46bc0
cmpq 0xe8(%rcx), %rax
jbe 0x23cf8
movl 0x38(%r15), %esi
movl $0xe10, %eax            # imm = 0xE10
xorl %edx, %edx
divl 0x108(%rcx)
leal 0x2(,%rax,2), %ecx
incl %eax
movl %esi, %edx
shrl %edx
cmpl $0x1, %edx
adcl $0x0, %edx
movl %esi, %edi
subl %eax, %edi
cmpl %ecx, %esi
cmovbl %edx, %edi
movl %edi, 0xc(%rsp)
leaq 0x22e16(%rip), %rax     # 0x46a98
movl (%rax), %eax
xorl %esi, %esi
testl %eax, %eax
setg %sil
leaq 0x10918(%rip), %rdx     # 0x345ab
leaq 0x10(%rsp), %r14
movq %r14, %rdi
callq 0x9718
cmpb $0x1, (%r14)
jne 0x23cbc
movq 0x18(%rsp), %rdi
leaq 0x12f57(%rip), %rsi     # 0x36c09
movl $0x13, %edx
callq 0x7580
cmpb $0x1, 0x10(%rsp)
jne 0x23cd1
movq 0x18(%rsp), %rdi
movl 0xc(%rsp), %esi
callq 0x7330
cmpb $0x1, 0x10(%rsp)
jne 0x23cee
movq 0x18(%rsp), %rdi
leaq 0x12f39(%rip), %rsi     # 0x36c1d
movl $0x7, %edx
callq 0x7580
leaq 0x10(%rsp), %rdi
callq 0x8f94
movq 0x10(%rbx), %rax
movq 0x8(%rax), %r14
cmpq 0x10(%rax), %r14
je 0x23e4a
movq $0x0, 0x30(%rsp)
leaq 0x10(%rsp), %r15
movl $0x0, 0x90(%rbx)
xorps %xmm0, %xmm0
movaps %xmm0, 0x10(%rsp)
movaps %xmm0, 0x20(%rsp)
movq %rbx, 0x10(%rsp)
movb %r13b, 0x18(%rsp)
movl 0xc(%rsp), %eax
movl %eax, 0x1c(%rsp)
leaq 0x164(%rip), %rax       # 0x23eac
movq %rax, 0x28(%rsp)
leaq 0x34c(%rip), %rax       # 0x240a0
movq %rax, 0x20(%rsp)
movq %r14, %rdi
movq %r15, %rsi
callq 0x240fc
movq 0x20(%rsp), %rax
testq %rax, %rax
je 0x23d7b
movq %r15, %rdi
movq %r15, %rsi
movl $0x3, %edx
callq *%rax
movq 0x98(%rbx), %rbp
movq 0xa0(%rbx), %r12
cmpq %rbp, %r12
je 0x23db4
movl %r13d, %r15d
movq %rbp, %r13
movq %r13, %rdi
callq 0x182b0
addq $0x4, %r13
cmpq %r12, %r13
jne 0x23d94
movq %rbp, 0xa0(%rbx)
movl %r15d, %r13d
leaq 0x10(%rsp), %r15
movq 0x30(%rbx), %rax
cmpq 0x38(%rbx), %rax
je 0x23e38
leaq 0x22dfb(%rip), %rax     # 0x46bc0
movq 0x30(%rsp), %rsi
addq 0xf8(%rax), %rsi
cmpq $0x2710, %rsi           # imm = 0x2710
jb 0x23e33
testq %rsi, %rsi
jle 0x23e28
movq %rsi, %rax
movabsq $0x431bde82d7b634db, %rcx # imm = 0x431BDE82D7B634DB
mulq %rcx
shrq $0x12, %rdx
imulq $-0xf4240, %rdx, %rax   # imm = 0xFFF0BDC0
addq %rsi, %rax
imulq $0x3e8, %rax, %rax      # imm = 0x3E8
movq %rdx, 0x10(%rsp)
movq %rax, 0x18(%rsp)
movq %r15, %rdi
movq %r15, %rsi
callq 0x7110
cmpl $-0x1, %eax
jne 0x23e28
callq 0x7040
cmpl $0x4, (%rax)
je 0x23e0e
movq $0x0, 0x30(%rsp)
jmp 0x23e38
movq %rsi, 0x30(%rsp)
addq $0x30, %r14
movq 0x10(%rbx), %rax
cmpq 0x10(%rax), %r14
jne 0x23d18
testb %r13b, %r13b
je 0x23e5f
xorl %eax, %eax
leaq 0x23068(%rip), %rcx     # 0x46ec0
xchgq %rax, 0xc0(%rcx)
addq $0x38, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
movq %rax, %rbx
leaq 0x10(%rsp), %rdi
callq 0x8f94
jmp 0x23e9b
jmp 0x23ea3
movq %rax, %rbx
movq 0x20(%rsp), %rax
testq %rax, %rax
je 0x23e9b
leaq 0x10(%rsp), %rdi
movq %rdi, %rsi
movl $0x3, %edx
callq *%rax
movq %rbx, %rdi
callq 0x79b0
movq %rax, %rdi
callq 0x8fc6
nop