github.com/hata/goseq@v0.0.0-20150316021154-a5ca66a92399/executor.go (about)

     1  package goseq
     2  
     3  import (
     4  	"sync"
     5  )
     6  
     7  
     8  const (
     9  	closeExecutorIndex = -1
    10  )
    11  
    12  type Any interface{}
    13  
    14  type Job func() (Any, error)
    15  
    16  type Executor interface {
    17  	Max() int
    18  	Execute(runnable Job) Future
    19  	Stop()
    20  }
    21  
    22  type executor struct {
    23  	lock                sync.Mutex
    24  	max                 int
    25  	freeJobIndexChannel chan int
    26  	jobIndexChannel     chan int
    27  	resultChannels      []chan Future
    28  	jobs                []Job
    29  	waitingStop         sync.WaitGroup
    30  }
    31  
    32  type Future interface {
    33  	Result() (Any, error)
    34  }
    35  
    36  type future struct {
    37  	finished    bool
    38  	waitChannel chan bool
    39  	result      Any
    40  	err         error
    41  }
    42  
    43  func NewExecutor(max int) Executor {
    44  	return newExecutor(max)
    45  }
    46  
    47  func newExecutor(max int) (ex *executor) {
    48  	ex = new(executor)
    49  	ex.max = max
    50  	ex.jobs = make([]Job, max)
    51  	ex.startWorkers()
    52  	return ex
    53  }
    54  
    55  func (ex *executor) startWorkers() {
    56  	ex.jobIndexChannel = make(chan int)
    57  	ex.resultChannels = make([]chan Future, ex.max)
    58  	ex.freeJobIndexChannel = make(chan int, ex.max)
    59  	ex.waitingStop.Add(ex.max)
    60  	for i := 0; i < ex.max; i++ {
    61  		ex.resultChannels[i] = make(chan Future, 1)
    62  		go ex.startWorker()
    63  		ex.freeJobIndexChannel <- i
    64  	}
    65  }
    66  
    67  func (ex *executor) Stop() {
    68  	for i := 0;i < ex.max;i++ {
    69  		ex.jobIndexChannel <- closeExecutorIndex
    70  	}
    71  	close(ex.jobIndexChannel)
    72  	close(ex.freeJobIndexChannel)
    73  	for i := range ex.jobs {
    74  		ex.jobs[i] = nil
    75  	}
    76  	for _,ch := range ex.resultChannels {
    77  		close(ch)
    78  	}
    79  	ex.waitingStop.Wait()
    80  }
    81  
    82  func newFuture() (f *future) {
    83  	f = new(future)
    84  	f.finished = false
    85  	f.waitChannel = make(chan bool, 1)
    86  	return f
    87  }
    88  
    89  func (ex *executor) startWorker() {
    90  	defer ex.waitingStop.Done()
    91  	for {
    92  		jobIndex := <- ex.jobIndexChannel
    93  		if jobIndex < 0 {
    94  			break
    95  		}
    96  		f := newFuture()
    97  		ex.resultChannels[jobIndex] <- f
    98  		f.result, f.err = ex.jobs[jobIndex]()
    99  		f.waitChannel <- true
   100  		close(f.waitChannel)
   101  		ex.addFreeJobIndexChannel(jobIndex)
   102  	}
   103  }
   104  
   105  // I'm not sure writing to channel needs to lock or not.
   106  func (ex *executor) addFreeJobIndexChannel(jobIndex int) {
   107  	ex.lock.Lock()
   108  	defer ex.lock.Unlock()
   109  	ex.freeJobIndexChannel <- jobIndex
   110  }
   111  
   112  func (ex *executor) Max() int {
   113  	return ex.max
   114  }
   115  
   116  func (ex *executor) Execute(job Job) Future {
   117  	index := <-ex.freeJobIndexChannel
   118  	ex.jobs[index] = job
   119  	ex.jobIndexChannel <- index
   120  	return <-ex.resultChannels[index]
   121  }
   122  
   123  func (f *future) Result() (Any, error) {
   124  	if !f.finished {
   125  		f.finished = <-f.waitChannel
   126  	}
   127  	return f.result, f.err
   128  }