
@ignore_access

header "sys" as sys

use gc
use coro

#if OS != win
use mem
#end

global current_thread: ?Thread (null)

+ fn start(func: fn()()) Thread !start {
    let t = Thread {
        handler: func
    }
    t.init() !>
    return t
}

+ class Thread {
    - thread: ?ptr
    - handler: fn()()
    ~ finished: bool (false)

    + static fn start(func: fn()()) SELF !start {
        let t = SELF {
            handler: func
        }
        t.init() !>
        return t
    }
    + static fn start_unsafe(func: fn()()) SELF !start {
        let t = SELF {
            handler: func
        }
        t.init() !>
        return t
    }

    fn init() !start $undefined {

        gc:share(this)

        #if OS == win
        this.thread = sys:CreateThread(null, 0, this.entry, this, 0, null)
        #else
        let thr = sys:pthread_t{ data: 0 }
        let err = sys:pthread_create(thr, null, this.entry, this)
        if err != 0 : throw start
        this.thread = thr
        #end
    }

    fn entry() $entrance {

        // Init
        gc:stack_top = gc:stack_root
        gc:thread_init()

        @allocas

        // Store globals & Set default value (local only)
        #loop globals G T
        #if !global_is_shared(G)
        G = @global_default_value(G)
        #if is_gc_type(T)
        gc:gc.globals.add_ptr(@ref(G))
        #end
        #end
        #end

        current_thread = this
        gc:gc.disable = false

        // Run
        let run_until = (co this.handler()).@cast(coro:Coro)
        coro:Coro.loop(run_until)

        // Stop
        this.finished = true
        gc:thread_stop()
    }

    + fn wait() {
        if this.finished : return
        let thr = this.thread
        if isset(thr) {
            gc:unlock()
            #if OS == win
            sys:WaitForSingleObject(thr, -1)
            #else
            sys:pthread_join((thr.@cast(sys:pthread_t)).data, null)
            #end
            gc:lock()
        }
    }

    fn gc_free() {
        let thr = this.thread
        if isset(thr) {
            #if OS == win
            sys:CloseHandle(thr)
            #else
            mem:free(thr)
            #end
        }
    }
}
