feat: 添加 WithDefaultTimeout 默认超时配置

Invoke 之前完全依赖调用方传入的 ctx 控制超时,池子被占满或 Python 侧
handler 阻塞时,未设置 deadline 的调用会永久阻塞且不报错。新增
WithDefaultTimeout 选项,仅在 ctx 未设置 deadline 时兜底生效,调用方
显式设置的超时优先级更高。

同时补充 example 中的阻塞/超时演示(demoTimeout、demoBlocking)和
pool_test.go 集成测试,覆盖池占满排队、默认超时、显式 deadline 优先级、
流式输出超时后 channel 静默关闭等场景。
This commit is contained in:
2026-07-23 10:37:24 +08:00
parent db71a904e0
commit 0f4a4ded53
7 changed files with 367 additions and 7 deletions
+22 -7
View File
@@ -9,6 +9,7 @@ import (
"reflect"
"sync"
"sync/atomic"
"time"
)
// poolConfig 是进程池内部配置,通过 Option 函数填充
@@ -23,6 +24,7 @@ type poolConfig struct {
stdout io.Writer
stderr io.Writer
handler any
defaultTimeout time.Duration
}
// Option 是 NewPool 的函数选项
@@ -74,6 +76,14 @@ func WithStderr(w io.Writer) Option {
return func(c *poolConfig) { c.stderr = w }
}
// WithDefaultTimeout 设置 Invoke 调用的默认超时时间(默认不启用)。
// 仅当调用方传入的 ctx 未设置 deadline 时才会生效,用于防止调用方忘记设置超时导致
// Invoke 无限期阻塞(例如所有 worker 连接被占满,或 Python 侧 handler 阻塞不返回)。
// 调用方通过 context.WithTimeout 显式设置的超时优先级更高,不会被覆盖。
func WithDefaultTimeout(d time.Duration) Option {
return func(c *poolConfig) { c.defaultTimeout = d }
}
// WithHandlers 注册 Go handler struct,其所有公开方法自动暴露给 Python 通过 call_go() 调用。
//
// type MyService struct{}
@@ -93,6 +103,7 @@ type Pool interface {
acquire(ctx context.Context) (net.Conn, *worker, error)
nextReqID() uint64
callbackDispatch(ctx context.Context, msg Message) (any, string)
defaultTimeout() time.Duration
}
// ── goHandler ────────────────────────────────────────────────────────────────
@@ -105,15 +116,15 @@ type goHandler struct {
hasErr bool
}
// ── pool(内部实现)─────────────────────────────────────────────────────────
type pool struct {
workers []*worker
idx atomic.Uint64
reqID atomic.Uint64
mu sync.RWMutex
handlers map[string]goHandler
workers []*worker
idx atomic.Uint64
reqID atomic.Uint64
mu sync.RWMutex
handlers map[string]goHandler
defTimeout time.Duration
}
// NewPool 创建并启动进程池。
@@ -151,7 +162,7 @@ func NewPool(script string, opts ...Option) (Pool, error) {
workers[i] = w
}
p := &pool{workers: workers, handlers: make(map[string]goHandler)}
p := &pool{workers: workers, handlers: make(map[string]goHandler), defTimeout: cfg.defaultTimeout}
if cfg.handler != nil {
p.bindHandlers(cfg.handler)
@@ -231,6 +242,10 @@ func (p *pool) nextReqID() uint64 {
return p.reqID.Add(1)
}
func (p *pool) defaultTimeout() time.Duration {
return p.defTimeout
}
func (p *pool) callbackDispatch(ctx context.Context, msg Message) (any, string) {
p.mu.RLock()
h, ok := p.handlers[msg.Method]