penguin/AstroJS

Javascript framework for my blog

resolver-go/internal/compilecache/cache.go

raw ยท 1423 bytes

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)
}