lolly/internal/lua/context.go
xfy 49f1e26760 refactor(lua): 对象池类型安全优化
- 使用 any 替代 interface{} (Go 1.18+)
- 添加类型断言检查防止 Pool 误用

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-16 11:13:53 +08:00

155 lines
3.7 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Package lua 提供 Lua 脚本嵌入能力
package lua
import (
"sync"
"github.com/valyala/fasthttp"
)
// LuaContext 请求级 Lua 上下文
//
// 类型命名说明:虽然 lua.LuaContext 存在 stuttering但保持此命名以
// 1) 与 LuaEngine/LuaCoroutine 保持一致的 API 命名风格
// 2) 明确区分 Lua 上下文与其他上下文类型(如 context.Context
// 3) 保持向后兼容性
type LuaContext struct {
Engine *LuaEngine
Coroutine *LuaCoroutine
RequestCtx *fasthttp.RequestCtx
Variables map[string]string
OutputBuffer []byte
Phase Phase
Exited bool
}
// luaContextPool LuaContext 对象池
var luaContextPool = sync.Pool{
New: func() any {
return &LuaContext{
Variables: make(map[string]string),
}
},
}
// AcquireContext 从池中获取 LuaContext
func AcquireContext(engine *LuaEngine, req *fasthttp.RequestCtx) *LuaContext {
v := luaContextPool.Get()
lc, ok := v.(*LuaContext)
if !ok {
// Pool 的 New 函数返回 *LuaContext类型断言不应失败
// 如果失败说明 Pool 被错误使用panic 是合理的
panic("luaContextPool returned unexpected type")
}
lc.Engine = engine
lc.RequestCtx = req
lc.Phase = PhaseInit
// Variables 和 OutputBuffer 已在 Release 中重置
return lc
}
// NewContext 创建请求上下文(从池中获取)
func NewContext(engine *LuaEngine, req *fasthttp.RequestCtx) *LuaContext {
return AcquireContext(engine, req)
}
// InitCoroutine 初始化协程
func (c *LuaContext) InitCoroutine() error {
coro, err := c.Engine.NewCoroutine(c.RequestCtx)
if err != nil {
return err
}
c.Coroutine = coro
return c.Coroutine.SetupSandbox()
}
// Execute 执行 Lua 脚本
func (c *LuaContext) Execute(script string) error {
if c.Coroutine == nil {
if err := c.InitCoroutine(); err != nil {
return err
}
}
return c.Coroutine.Execute(script)
}
// ExecuteFile 执行文件脚本
func (c *LuaContext) ExecuteFile(path string) error {
if c.Coroutine == nil {
if err := c.InitCoroutine(); err != nil {
return err
}
}
return c.Coroutine.ExecuteFile(path)
}
// SetPhase 设置当前阶段
func (c *LuaContext) SetPhase(phase Phase) {
c.Phase = phase
}
// GetPhase 获取当前阶段
func (c *LuaContext) GetPhase() Phase {
return c.Phase
}
// GetVariable 获取变量
func (c *LuaContext) GetVariable(name string) (string, bool) {
val, ok := c.Variables[name]
return val, ok
}
// SetVariable 设置变量
func (c *LuaContext) SetVariable(name, value string) {
c.Variables[name] = value
}
// Write 输出内容
func (c *LuaContext) Write(data []byte) {
c.OutputBuffer = append(c.OutputBuffer, data...)
}
// Say 输出内容并换行
func (c *LuaContext) Say(data string) {
c.OutputBuffer = append(c.OutputBuffer, data...)
c.OutputBuffer = append(c.OutputBuffer, '\n')
}
// Exit 退出请求处理
func (c *LuaContext) Exit(code int) {
c.Exited = true
c.RequestCtx.SetStatusCode(code)
}
// Release 释放资源并放回池中
func (c *LuaContext) Release() {
if c.Coroutine != nil {
c.Coroutine.Close()
c.Coroutine = nil
}
// 重置所有可变状态,防止请求间污染
for k := range c.Variables {
delete(c.Variables, k)
}
c.OutputBuffer = c.OutputBuffer[:0]
c.Phase = PhaseInit
c.Exited = false
c.Engine = nil
c.RequestCtx = nil
luaContextPool.Put(c)
}
// FlushOutput 刷新输出到响应
func (c *LuaContext) FlushOutput() {
if len(c.OutputBuffer) > 0 && c.RequestCtx != nil {
// Write 返回写入的字节数和可能的错误
// 在响应刷新场景中,我们选择忽略错误,因为:
// 1. fasthttp.RequestCtx.Write 内部已经处理了连接状态
// 2. 此阶段出错时请求处理已完成,无法向客户端报告
_, _ = c.RequestCtx.Write(c.OutputBuffer)
c.OutputBuffer = c.OutputBuffer[:0]
}
}