Concurrency patterns for Titus Scanners
mainBecause Titus uses Hyperscan internally, each titus.Scanner instance requires exclusive access to its scratch memory. Do not share a single scanner instance across multiple goroutines simultaneously.
Choose one of the following patterns for concurrent scanning:
Pattern 1: Multiple Scanner Instances
Create a new scanner per goroutine. This is the simplest approach but has higher setup overhead.
Pattern 2: Worker Pool with Scanner Pool
For high-throughput, maintain a channel of pre-initialized scanners. A worker pulls a scanner from the channel, performs the scan, and returns it when finished.
Pattern 3: Sequential Scanning
Use a single scanner instance to scan files one after another in a loop. This is safe and efficient for single-threaded workflows.
// Pattern 2: Worker Pool with Scanner Pool
type ScannerPool struct {
scanners chan *titus.Scanner
}
func NewScannerPool(size int) (*ScannerPool, error) {
pool := &ScannerPool{
scanners: make(chan *titus.Scanner, size),
}
for i := 0; i < size; i++ {
s, err := titus.NewScanner()
if err != nil {
return nil, err
}
pool.scanners <- s
}
return pool, nil
}
func (p *ScannerPool) Scan(content string) ([]*titus.Match, error) {
scanner := <-p.scanners
defer func() { p.scanners <- scanner }()
return scanner.ScanString(content)
}