Files

83 lines
1.7 KiB
Go

package auth
import (
"context"
"runtime"
"sync/atomic"
)
// Pool 是 argon2id 并发池(DEVELOPMENT 第 12 节)。
type Pool struct {
sem chan struct{}
waiting atomic.Int64
active atomic.Int64
maxSeen atomic.Int64 // 测试用:观察到的最大并发
}
// NewPool 创建大小为 CPU 核数的哈希池。
func NewPool() *Pool {
return NewPoolSize(runtime.NumCPU())
}
// NewPoolSize 创建指定并发上限的哈希池(测试可传入 1)。
func NewPoolSize(n int) *Pool {
if n < 1 {
n = 1
}
return &Pool{sem: make(chan struct{}, n)}
}
// Hash 在池内计算 PHC 格式哈希。
func (p *Pool) Hash(ctx context.Context, _ PasswordKind, password string) (string, error) {
if err := p.acquire(ctx); err != nil {
return "", err
}
defer p.release()
return HashPassword(password)
}
// Verify 在池内校验;常量时间比较。
func (p *Pool) Verify(ctx context.Context, _ PasswordKind, password, phc string) (bool, error) {
if err := p.acquire(ctx); err != nil {
return false, err
}
defer p.release()
return VerifyPassword(password, phc)
}
// QueueLen 返回等待获取池槽位的任务数。
func (p *Pool) QueueLen() int {
return int(p.waiting.Load())
}
// MaxActive 返回曾达到的最大并发哈希数(测试用)。
func (p *Pool) MaxActive() int {
return int(p.maxSeen.Load())
}
func (p *Pool) acquire(ctx context.Context) error {
p.waiting.Add(1)
select {
case p.sem <- struct{}{}:
p.waiting.Add(-1)
cur := p.active.Add(1)
for {
old := p.maxSeen.Load()
if cur <= old || p.maxSeen.CompareAndSwap(old, cur) {
break
}
}
return nil
case <-ctx.Done():
p.waiting.Add(-1)
return ctx.Err()
}
}
func (p *Pool) release() {
p.active.Add(-1)
<-p.sem
}
var _ HashPool = (*Pool)(nil)