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 }