From 1589d34385744e4b0d8a602f19ec48dfbc754f6a Mon Sep 17 00:00:00 2001 From: Eldo Loguzzo Date: Wed, 7 Oct 2026 15:16:21 -0600 Subject: [PATCH] Issue #546 : collectgarbage honors its option and returns a value "collect" (default) returns 0, "count" the Kbytes in use by the Go heap, "step" runs a collection and returns true, "stop"/"restart" return 0 (the Go collector cannot be stopped for one state), "setpause" and "setstepmul" keep the value and return the previous one (Lua 5.1 defaults 200). An unknown option is an argument error. Co-Authored-By: Claude Opus 5.5 (1M context) --- _glua-tests/base.lua | 11 +++++++++++ _state.go | 2 ++ baselib.go | 29 +++++++++++++++++++++++++++-- state.go | 2 ++ value.go | 2 ++ 5 files changed, 44 insertions(+), 2 deletions(-) diff --git a/_glua-tests/base.lua b/_glua-tests/base.lua index 2bc64a8b..f6ed976b 100644 --- a/_glua-tests/base.lua +++ b/_glua-tests/base.lua @@ -46,3 +46,14 @@ local ok, a, b = xpcall(function() return err .. "!", "b" end) assert(not ok and string.find(a, "error!!") and b == nil) + +-- collectgarbage options, as in Lua 5.1 (#546) +assert(collectgarbage() == 0) +assert(collectgarbage("collect") == 0) +local kb = collectgarbage("count") +assert(type(kb) == "number" and kb > 0) +assert(type(collectgarbage("step")) == "boolean") +assert(collectgarbage("stop") == 0 and collectgarbage("restart") == 0) +assert(collectgarbage("setpause", 150) == 200 and collectgarbage("setpause", 200) == 150) +assert(collectgarbage("setstepmul", 300) == 200 and collectgarbage("setstepmul", 200) == 300) +assert(not pcall(collectgarbage, "nope")) diff --git a/_state.go b/_state.go index 6e9febfb..4e0cb61f 100644 --- a/_state.go +++ b/_state.go @@ -543,6 +543,8 @@ func newGlobal() *Global { Global: newLTable(0, 64), builtinMts: make(map[int]LValue), tempFiles: make([]*os.File, 0, 10), + gcPause: 200, // Lua 5.1 defaults + gcStepMul: 200, } } diff --git a/baselib.go b/baselib.go index aa2c08a9..d62fb5e0 100644 --- a/baselib.go +++ b/baselib.go @@ -64,8 +64,33 @@ func baseAssert(L *LState) int { } func baseCollectGarbage(L *LState) int { - runtime.GC() - return 0 + switch opt := L.OptString(1, "collect"); opt { + case "collect": + runtime.GC() + L.Push(LNumber(0)) + case "count": + // Kbytes in use by the Go heap (the Lua state has no heap of its own). + var ms runtime.MemStats + runtime.ReadMemStats(&ms) + L.Push(LNumber(float64(ms.HeapAlloc) / 1024)) + case "step": + runtime.GC() + L.Push(LTrue) + case "stop", "restart": + // The Go collector cannot be stopped for one state. + L.Push(LNumber(0)) + case "setpause": + prev := L.G.gcPause + L.G.gcPause = L.OptInt(2, 0) + L.Push(LNumber(prev)) + case "setstepmul": + prev := L.G.gcStepMul + L.G.gcStepMul = L.OptInt(2, 0) + L.Push(LNumber(prev)) + default: + L.ArgError(1, "invalid option '"+opt+"'") + } + return 1 } func baseDoFile(L *LState) int { diff --git a/state.go b/state.go index 292f93b4..309d46a3 100644 --- a/state.go +++ b/state.go @@ -643,6 +643,8 @@ func newGlobal() *Global { Global: newLTable(0, 64), builtinMts: make(map[int]LValue), tempFiles: make([]*os.File, 0, 10), + gcPause: 200, // Lua 5.1 defaults + gcStepMul: 200, } } diff --git a/value.go b/value.go index 4156e9d5..5dbc90d7 100644 --- a/value.go +++ b/value.go @@ -174,6 +174,8 @@ type Global struct { builtinMts map[int]LValue tempFiles []*os.File gccount int32 + gcPause int // collectgarbage("setpause"): kept and returned, no effect on the Go collector + gcStepMul int // collectgarbage("setstepmul"): idem } type LState struct {