package compilecache import ( "context" ) func New(compiler Compiler, workers int, queueSize int) *Cache { if workers <= 0 { workers = 1 } if queueSize <= 0 { queueSize = 128 } c := &Cache{ compiler: compiler, states: make(map[string]State), results: make(map[string][]byte), errors: make(map[string]error), jobs: make(chan Job, queueSize), } for i := 0; i < workers; i++ { go c.worker() } return c } func (c *Cache) Get(hash string) (State, []byte, error) { 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 { c.mu.Lock() state, ok := c.states[job.Hash] if ok { c.mu.Unlock() return state } c.states[job.Hash] = Pending c.mu.Unlock() // Do not hold the lock while sending. c.jobs <- job return Pending } 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) }