// Copyright (c) 2026 Charles KWON OhJun (charleskwonohjun@gmail.com) // All rights reserved. // RTL goroutine functions for PRG code: // GO(bBlock|cFunc, ...) — launch goroutine // CHANNEL([nSize]) — create channel // CHSEND(ch, xValue) — send to channel // CHRECEIVE(ch) ��� receive from channel // CHCLOSE(ch) — close channel // WAITGROUP(nCount) — create WaitGroup // WGDONE(wg) — WaitGroup.Done() // WGWAIT(wg) — WaitGroup.Wait() // WGADD(wg, n) — WaitGroup.Add(n) // MUTEX() — create Mutex // LOCK(mtx) — Mutex.Lock() // UNLOCK(mtx) — Mutex.Unlock() // SLEEP(nSeconds) — time.Sleep package hbrtl import ( "five/hbrt" "time" ) // GO(bBlock [, args...]) — launch goroutine with code block or function // Returns NIL immediately (goroutine runs in background). func GoFunc(t *hbrt.Thread) { nParams := t.ParamCount() t.Frame(nParams, 0) defer t.EndProc() first := t.Local(1) // Collect args args := make([]hbrt.Value, 0, nParams-1) for i := 2; i <= nParams; i++ { args = append(args, t.Local(i)) } vm := t.VM() if first.IsBlock() { blk := first.AsBlock() vm.GoLaunchBlock(blk, args) } else if first.IsString() { // Function name as string — find symbol and launch sym := vm.FindSymbol(first.AsString()) if sym != nil && sym.Func != nil { vm.GoLaunch(sym.Func, args) } } t.RetNil() } // CHANNEL([nBufferSize]) → pChannel func ChannelFunc(t *hbrt.Thread) { nParams := t.ParamCount() t.Frame(nParams, 0) defer t.EndProc() size := 0 if nParams >= 1 && !t.Local(1).IsNil() { size = t.Local(1).AsInt() } t.RetVal(hbrt.MakeChannel(size)) } // CHSEND(pChannel, xValue) → NIL func ChSend(t *hbrt.Thread) { t.Frame(2, 0) defer t.EndProc() ch := t.Local(1).AsChannel() if ch == nil { t.RetNil() return } ch.Send(t.Local(2)) t.RetNil() } // CHRECEIVE(pChannel) → xValue func ChReceive(t *hbrt.Thread) { t.Frame(1, 0) defer t.EndProc() ch := t.Local(1).AsChannel() if ch == nil { t.RetNil() return } t.RetVal(ch.Receive()) } // CHCLOSE(pChannel) → NIL func ChClose(t *hbrt.Thread) { t.Frame(1, 0) defer t.EndProc() ch := t.Local(1).AsChannel() if ch != nil { ch.Close() } t.RetNil() } // WAITGROUP([nCount]) → pWaitGroup func WaitGroupFunc(t *hbrt.Thread) { nParams := t.ParamCount() t.Frame(nParams, 0) defer t.EndProc() n := 0 if nParams >= 1 && !t.Local(1).IsNil() { n = t.Local(1).AsInt() } t.RetVal(hbrt.MakeWaitGroup(n)) } // WGDONE(pWaitGroup) �� NIL func WgDone(t *hbrt.Thread) { t.Frame(1, 0) defer t.EndProc() wg := t.Local(1).AsWaitGroup() if wg != nil { wg.WG.Done() } t.RetNil() } // WGWAIT(pWaitGroup) → NIL func WgWait(t *hbrt.Thread) { t.Frame(1, 0) defer t.EndProc() wg := t.Local(1).AsWaitGroup() if wg != nil { wg.WG.Wait() } t.RetNil() } // WGADD(pWaitGroup, nDelta) → NIL func WgAdd(t *hbrt.Thread) { t.Frame(2, 0) defer t.EndProc() wg := t.Local(1).AsWaitGroup() if wg != nil { wg.WG.Add(t.Local(2).AsInt()) } t.RetNil() } // MUTEX() → pMutex func MutexFunc(t *hbrt.Thread) { t.Frame(0, 0) defer t.EndProc() t.RetVal(hbrt.MakeMutex()) } // LOCK(pMutex) → NIL func LockFunc(t *hbrt.Thread) { t.Frame(1, 0) defer t.EndProc() mu := t.Local(1).AsMutex() if mu != nil { mu.Mu.Lock() } t.RetNil() } // UNLOCK(pMutex) → NIL func UnlockFunc(t *hbrt.Thread) { t.Frame(1, 0) defer t.EndProc() mu := t.Local(1).AsMutex() if mu != nil { mu.Mu.Unlock() } t.RetNil() } // SLEEP(nSeconds) → NIL // Supports fractional seconds: Sleep(0.1) = 100ms func SleepFunc(t *hbrt.Thread) { t.Frame(1, 0) defer t.EndProc() val := t.Local(1) var dur time.Duration if val.IsNumInt() { dur = time.Duration(val.AsLong()) * time.Second } else { dur = time.Duration(val.AsNumDouble() * float64(time.Second)) } time.Sleep(dur) t.RetNil() }