github.com/TIBCOSoftware/flogo-lib@v0.5.9/engine/runner/pooled.go (about) 1 package runner 2 3 import ( 4 "context" 5 "errors" 6 7 "github.com/TIBCOSoftware/flogo-lib/core/action" 8 "github.com/TIBCOSoftware/flogo-lib/core/data" 9 "github.com/TIBCOSoftware/flogo-lib/logger" 10 ) 11 12 // PooledRunner is a action runner that queues and runs a action in a worker pool 13 type PooledRunner struct { 14 workerQueue chan chan ActionWorkRequest 15 workQueue chan ActionWorkRequest 16 numWorkers int 17 workers []*ActionWorker 18 active bool 19 20 directRunner *DirectRunner 21 } 22 23 // PooledConfig is the configuration object for a PooledRunner 24 type PooledConfig struct { 25 NumWorkers int `json:"numWorkers"` 26 WorkQueueSize int `json:"workQueueSize"` 27 } 28 29 // NewPooledRunner create a new pooled 30 func NewPooled(config *PooledConfig) *PooledRunner { 31 32 var pooledRunner PooledRunner 33 pooledRunner.directRunner = NewDirect() 34 35 // config via engine config 36 pooledRunner.numWorkers = config.NumWorkers 37 pooledRunner.workQueue = make(chan ActionWorkRequest, config.WorkQueueSize) 38 39 return &pooledRunner 40 } 41 42 // Start will start the engine, by starting all of its workers 43 func (runner *PooledRunner) Start() error { 44 45 if !runner.active { 46 47 runner.workerQueue = make(chan chan ActionWorkRequest, runner.numWorkers) 48 49 runner.workers = make([]*ActionWorker, runner.numWorkers) 50 51 for i := 0; i < runner.numWorkers; i++ { 52 id := i + 1 53 logger.Debugf("Starting worker with id '%d'", id) 54 worker := NewWorker(id, runner.directRunner, runner.workerQueue) 55 runner.workers[i] = &worker 56 worker.Start() 57 } 58 59 go func() { 60 for { 61 select { 62 case work := <-runner.workQueue: 63 logger.Debug("Received work request") 64 65 //todo fix, this creates unbounded go routines waiting to be serviced by worker queue 66 go func() { 67 worker := <-runner.workerQueue 68 69 logger.Debug("Dispatching work request") 70 worker <- work 71 }() 72 } 73 } 74 }() 75 76 runner.active = true 77 } 78 79 return nil 80 } 81 82 // Stop will stop the engine, by stopping all of its workers 83 func (runner *PooledRunner) Stop() error { 84 85 if runner.active { 86 87 runner.active = false 88 89 for _, worker := range runner.workers { 90 logger.Debug("Stopping worker", worker.ID) 91 worker.Stop() 92 } 93 } 94 95 return nil 96 } 97 98 // Deprecated: Use Execute() instead 99 func (runner *PooledRunner) Run(ctx context.Context, act action.Action, uri string, options interface{}) (code int, data interface{}, err error) { 100 101 return 0, nil, errors.New("unsupported") 102 } 103 104 // Deprecated: Use Execute() instead 105 func (runner *PooledRunner) RunAction(ctx context.Context, act action.Action, options map[string]interface{}) (results map[string]*data.Attribute, err error) { 106 107 return nil, errors.New("unsupported") 108 } 109 110 // Execute implements action.Runner.Execute 111 func (runner *PooledRunner) Execute(ctx context.Context, act action.Action, inputs map[string]*data.Attribute) (results map[string]*data.Attribute, err error) { 112 113 if act == nil { 114 return nil, errors.New("Action not specified") 115 } 116 117 if runner.active { 118 119 actionData := &ActionData{context: ctx, action: act, inputs: inputs, arc: make(chan *ActionResult, 1)} 120 work := ActionWorkRequest{ReqType: RtRun, actionData: actionData} 121 122 md := action.GetMetadata(act) 123 124 runner.workQueue <- work 125 logger.Debugf("Action '%s' queued", md.ID) 126 127 reply := <-actionData.arc 128 logger.Debugf("Action '%s' returned", md.ID) 129 130 return reply.results, reply.err 131 } 132 133 //Run rejected 134 return nil, errors.New("Runner not active") 135 }