83 lines
1.7 KiB
Go
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)
|