- Align struct fields and constants in gjson/config.go - Add missing newline at EOF in gjson/decode.go - Remove trailing blank line in gjson/encode.go - Remove extra blank line in internal/lua/coroutine.go - Use modern for range syntax in internal/lua/pool.go Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
153 lines
3.4 KiB
Go
153 lines
3.4 KiB
Go
// Package lua 提供 Lua 脚本嵌入能力。
|
||
//
|
||
// 该文件实现 LState 池,用于解决 gopher-lua 并发竞态问题。
|
||
// 每个请求从池中获取完全独立的 LState(各自拥有独立的 Global 对象),
|
||
// 彻底消除共享状态导致的 concurrent map read/write 问题。
|
||
//
|
||
// 设计要点:
|
||
// - 每个 LState 独立创建,拥有独立的 Global 表
|
||
// - 池化管理,避免频繁创建/销毁开销
|
||
// - 动态伸缩,支持最大容量限制
|
||
// - 工厂函数模式,统一 LState 初始化逻辑
|
||
//
|
||
// 作者:xfy
|
||
package lua
|
||
|
||
import (
|
||
"sync"
|
||
|
||
glua "github.com/yuin/gopher-lua"
|
||
)
|
||
|
||
// LStatePool 管理 LState 池。
|
||
//
|
||
// 每个请求从池中获取完全独立的 LState,彻底消除共享状态。
|
||
// 池支持动态伸缩,初始预热一定数量,运行时按需创建,
|
||
// 最大容量限制防止资源耗尽。
|
||
type LStatePool struct {
|
||
mu sync.Mutex
|
||
pool []*glua.LState
|
||
factory func() *glua.LState
|
||
maxSize int
|
||
current int
|
||
}
|
||
|
||
// NewLStatePool 创建 LState 池。
|
||
//
|
||
// 参数:
|
||
// - factory: LState 工厂函数,负责创建并初始化独立 LState
|
||
// - initialSize: 初始池大小(预热数量)
|
||
// - maxSize: 最大池大小(防止资源耗尽)
|
||
//
|
||
// 返回值:
|
||
// - *LStatePool: 池实例
|
||
func NewLStatePool(factory func() *glua.LState, initialSize, maxSize int) *LStatePool {
|
||
p := &LStatePool{
|
||
pool: make([]*glua.LState, 0, maxSize),
|
||
factory: factory,
|
||
maxSize: maxSize,
|
||
current: 0,
|
||
}
|
||
|
||
// 预热:预先创建 initialSize 个 LState
|
||
for range initialSize {
|
||
L := p.factory()
|
||
p.pool = append(p.pool, L)
|
||
p.current++
|
||
}
|
||
|
||
return p
|
||
}
|
||
|
||
// Get 从池中获取一个 LState。
|
||
//
|
||
// 如果池中有可用 LState,直接返回;否则创建新的 LState(不超过 maxSize)。
|
||
//
|
||
// 返回值:
|
||
// - *glua.LState: 可用的 LState 实例
|
||
// - error: 池已满时返回错误
|
||
func (p *LStatePool) Get() *glua.LState {
|
||
p.mu.Lock()
|
||
defer p.mu.Unlock()
|
||
|
||
// 池中有可用 LState
|
||
if len(p.pool) > 0 {
|
||
// 取出最后一个(避免数组移动)
|
||
n := len(p.pool) - 1
|
||
L := p.pool[n]
|
||
p.pool[n] = nil // 防止内存泄漏
|
||
p.pool = p.pool[:n]
|
||
return L
|
||
}
|
||
|
||
// 池为空,检查是否可以创建新的
|
||
if p.current >= p.maxSize {
|
||
// 已达最大容量,等待或返回 nil
|
||
// 这里选择返回 nil,由调用方处理
|
||
return nil
|
||
}
|
||
|
||
// 创建新的 LState
|
||
L := p.factory()
|
||
p.current++
|
||
return L
|
||
}
|
||
|
||
// Put 将 LState 归还池中。
|
||
//
|
||
// 归还前应确保 LState 处于干净状态(无残留数据)。
|
||
// 如果池已满(归还后超过 maxSize),直接关闭 LState。
|
||
//
|
||
// 参数:
|
||
// - L: 要归还的 LState
|
||
func (p *LStatePool) Put(L *glua.LState) {
|
||
if L == nil {
|
||
return
|
||
}
|
||
|
||
p.mu.Lock()
|
||
defer p.mu.Unlock()
|
||
|
||
// 检查池容量
|
||
if len(p.pool) >= p.maxSize {
|
||
// 池已满,直接关闭
|
||
L.Close()
|
||
p.current--
|
||
return
|
||
}
|
||
|
||
// 归还到池中
|
||
p.pool = append(p.pool, L)
|
||
}
|
||
|
||
// Close 关闭池,释放所有 LState。
|
||
//
|
||
// 关闭所有池中的 LState,并清空池。
|
||
func (p *LStatePool) Close() {
|
||
p.mu.Lock()
|
||
defer p.mu.Unlock()
|
||
|
||
// 关闭所有 LState
|
||
for _, L := range p.pool {
|
||
if L != nil {
|
||
L.Close()
|
||
}
|
||
}
|
||
p.pool = nil
|
||
p.current = 0
|
||
}
|
||
|
||
// Size 返回池当前大小(包括已借出的)。
|
||
func (p *LStatePool) Size() int {
|
||
p.mu.Lock()
|
||
defer p.mu.Unlock()
|
||
return p.current
|
||
}
|
||
|
||
// Available 返回池中可用的 LState 数量。
|
||
func (p *LStatePool) Available() int {
|
||
p.mu.Lock()
|
||
defer p.mu.Unlock()
|
||
return len(p.pool)
|
||
}
|