Files
2026-06-16 08:33:04 +08:00

108 lines
1.8 KiB
Go

package compilecache
import (
"context"
)
func New(compiler Compiler, opt Options) *Cache {
if opt.Workers <= 0 {
opt.Workers = 1
}
if opt.QueueSize <= 0 {
opt.QueueSize = 128
}
c := &Cache{
compiler: compiler,
disableCache: opt.DisableCache,
states: make(map[string]State),
results: make(map[string][]byte),
errors: make(map[string]error),
jobs: make(chan Job, opt.QueueSize),
}
if !opt.DisableCache {
for i := 0; i < opt.Workers; i++ {
go c.worker()
}
}
return c
}
func (c *Cache) Get(hash string) (State, []byte, error) {
if c.disableCache {
return Missing, nil, nil
}
c.mu.Lock()
defer c.mu.Unlock()
state, ok := c.states[hash]
if !ok {
return Missing, nil, nil
}
switch state {
case Ready:
return Ready, c.results[hash], nil
case Failed:
return Failed, nil, c.errors[hash]
default:
return state, nil, nil
}
}
func (c *Cache) Enqueue(job Job) State {
if c.disableCache {
// Pretend every request is new.
// Caller will see Pending forever unless something else reads result,
// so this only works if your caller does not rely on cache state.
go c.runUncached(job)
return Pending
}
c.mu.Lock()
state, ok := c.states[job.Hash]
if ok {
c.mu.Unlock()
return state
}
c.states[job.Hash] = Pending
c.mu.Unlock()
c.jobs <- job
return Pending
}
func (c *Cache) runUncached(job Job) {
_, _ = c.compiler.Compile(context.Background(), job)
}
func (c *Cache) worker() {
for job := range c.jobs {
c.run(job)
}
}
func (c *Cache) run(job Job) {
result, err := c.compiler.Compile(context.Background(), job)
c.mu.Lock()
defer c.mu.Unlock()
if err != nil {
c.states[job.Hash] = Failed
c.errors[job.Hash] = err
delete(c.results, job.Hash)
return
}
c.states[job.Hash] = Ready
c.results[job.Hash] = result
delete(c.errors, job.Hash)
}