package media import ( "context" "errors" "time" "go.uber.org/zap" ) // WorkerPool is a pool of workers that can consume and run a queue of tasks. type WorkerPool struct { size int ch chan func() logger *zap.SugaredLogger } // NewWorkerPool returns a new WorkerPool containing the specified number of // workers, and with the provided maximum queue size. Jobs added to the queue // after it reaches this size limit will be rejected. func NewWorkerPool(size int, maxQueueSize int, logger *zap.SugaredLogger) *WorkerPool { return &WorkerPool{ size: size, ch: make(chan func(), maxQueueSize), logger: logger, } } // NewTestWorkerPool returns a new running WorkerPool with a single worker, // and noop logger, suitable for test environments. func NewTestWorkerPool() *WorkerPool { p := NewWorkerPool(1, 256, zap.NewNop().Sugar()) p.Run() return p } // Run launches the workers, and returns immediately. func (p *WorkerPool) Run() { for i := 0; i < p.size; i++ { go func() { for task := range p.ch { task() } }() } } // WaitForTask blocks while the provided task is executed by a worker, // returning the error returned by the task. func (p *WorkerPool) WaitForTask(ctx context.Context, taskFunc func() error) error { done := make(chan error) queuedAt := time.Now() fn := func() { startedAt := time.Now() result := taskFunc() durTotal := time.Since(queuedAt) durTask := time.Since(startedAt) durQueue := startedAt.Sub(queuedAt) p.logger.With("task", durTask, "queue", durQueue, "total", durTotal).Infof("Completed task") done <- result } select { case p.ch <- fn: return <-done case <-ctx.Done(): return ctx.Err() default: return errors.New("worker queue full") } }