
@ignore_access

use core

// Core
@shared shared_lock: core:MutexStruct[void] (@undefined)
@shared gc_list: Array[Gc] (@undefined)
// Marks
@shared mark: u16 (1)
// Shared - Counters
shared mem_shared: uint
shared mem_shared_trigger: uint
shared running_shared: bool (@undefined)

+ fn collect_shared() {

    if gc.disable : return

    let m = mark
    gc.unlock()
    shared_lock.lock()

    if m != mark {
        shared_lock.unlock()
        gc.lock()
        return
    }

    let mygc = gc
    let gcs = gc_list

    each gcs as gcx {
        gcx.lock()
        if gcx.dont_stop : panic("GC stopped while calling share")
    }

    // Switch the mark after all gcs are locked
    if mark < 2 : mark = 2
    else : mark = 1
    running_shared = true
    // let msb = mem_shared / 1024

    // Collect will make sure all co_count are up-to-date
    // And update the marks of shared items on the stack
    each gcs as gcx {
        gc = gcx
        gcx.mark_shared = true
        gcx.collect()
    }
    // Update marks
    each gcs as gcx {
        gc = gcx
        gcx.update_marks()
    }
    // We remove all items that have the wrong mark or co_count == 0 & !owned
    each gcs as gcx {
        gc = gcx
        gcx.clear_shared()
    }
    // Check if we can clear items from the dump
    check_shared_dump()
    // Reset pools last
    each gcs as gcx {
        gc = gcx
        if verify : gcx.leak_check()
        reset_pools(gcx.pools, gcx.poolc_ptr[0])
    }

    //
    gc = mygc
    running_shared = false

    // let msa = mem_shared / 1024
    // let tb = mem_shared_trigger / 1024
    update_shared_trigger()
    // println("mark: %mark | msb: %msb | msa: %msa | freed: %{msb-msa} | tb: %tb | ta: %{mem_shared_trigger / 1024}")

    each gcs as gcx {
        gcx.unlock()
    }

    shared_lock.unlock()
    gc.lock()
}

fn update_shared_trigger() {
    let high = mem_shared / 100 * 130 + mem_minimum_trigger
    let low = mem_shared_trigger / 100 * 80
    if low > high : high = low
    mem_shared_trigger = high
}

fn free_shared_item(item: GcItem) {

    // Load data before clear
    let data = gcdata(item)
    let has_vtable = data.has_vtable()

    // Clear data
    data.co_count = 0
    data.flags = 0
    data.remove_mark()

    let offset = data.block_offset & offmask
    if offset > 0 {
        let block = item - offset
        @ptrv(block, uint, 2)--
        atomic(mem_shared - @ptrv(block, uint, 4))

        if has_vtable {
            // gc_free
            let func = @ptrv(data.vtable, ?fnRef(ptr)(), 1)
            if isset(func) : func(item)
        }
    }
}
