This repository has been archived by the owner on Sep 4, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
112 additions
and
62 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
package main | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"log" | ||
|
||
"github.com/skroutz/mistry/pkg/types" | ||
) | ||
|
||
// WorkerPool implements a fixed-size pool of worker goroutines that can be sent | ||
// build jobs and communicate their result | ||
type WorkerPool struct { | ||
MaxWorkers int | ||
QueueSize int | ||
queue chan workItem | ||
} | ||
|
||
// WorkResult contains the result of a job, either a buildinfo or an error | ||
type WorkResult struct { | ||
BuildInfo *types.BuildInfo | ||
Err error | ||
} | ||
|
||
// FutureWorkResult is a WorkResult that has not yet become available | ||
type FutureWorkResult struct { | ||
resultQueue <-chan WorkResult | ||
} | ||
|
||
// Wait waits for the result to become available and returns it | ||
func (future FutureWorkResult) Wait() (WorkResult, error) { | ||
result, ok := <-future.resultQueue | ||
if !ok { | ||
return WorkResult{}, fmt.Errorf("Failed to read from result channel") | ||
} | ||
return result, nil | ||
} | ||
|
||
// workItem contains a job and a channel to place the job result | ||
type workItem struct { | ||
job *Job | ||
jobRequest types.JobRequest | ||
result chan<- WorkResult | ||
} | ||
|
||
// NewWorkerPool creates a new worker pool | ||
func NewWorkerPool(s *Server, maxWorkers, queueSize int, logger *log.Logger) *WorkerPool { | ||
p := new(WorkerPool) | ||
p.MaxWorkers = maxWorkers | ||
p.QueueSize = queueSize | ||
p.queue = make(chan workItem, queueSize) | ||
|
||
for i := 0; i < maxWorkers; i++ { | ||
go poolWorker(s, i, p.queue) | ||
} | ||
logger.Printf("Set up %d workers", maxWorkers) | ||
return p | ||
} | ||
|
||
// Stop stops the pool workers and closes the queue | ||
func (p *WorkerPool) Stop() { | ||
close(p.queue) | ||
} | ||
|
||
// SendWork sends work to the pool, and receive a work result that can be waited on | ||
func (p *WorkerPool) SendWork(j *Job, jr types.JobRequest) (FutureWorkResult, error) { | ||
resultQueue := make(chan WorkResult, 1) | ||
wi := workItem{j, jr, resultQueue} | ||
result := FutureWorkResult{resultQueue} | ||
|
||
select { | ||
case p.queue <- wi: | ||
return result, nil | ||
default: | ||
return result, fmt.Errorf("queue is full") | ||
} | ||
} | ||
|
||
// poolWorker listens to the workQueue, runs Work() on any incoming work items, and | ||
// sends the result through the result queue | ||
func poolWorker(s *Server, id int, workQueue <-chan workItem) { | ||
logPrefix := fmt.Sprintf("[worker %d]", id) | ||
for item := range workQueue { | ||
s.Log.Printf("%s received work item %#v", logPrefix, item) | ||
buildInfo, err := s.Work(context.Background(), item.job, item.jobRequest) | ||
|
||
select { | ||
case item.result <- WorkResult{buildInfo, err}: | ||
s.Log.Printf("%s wrote result to the result channel", logPrefix) | ||
default: | ||
// this is a bug, should log error or panic here | ||
s.Log.Printf("Failed to write result to the result channel") | ||
} | ||
close(item.result) | ||
} | ||
s.Log.Printf("%s exiting...", logPrefix) | ||
} |