github.com/TIBCOSoftware/flogo-lib@v0.5.9/engine/runner/worker.go (about) 1 package runner 2 3 import ( 4 "context" 5 "fmt" 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 // Based off: http://nesv.github.io/golang/2014/02/25/worker-queues-in-go.html 13 14 // RequestType is value that indicates the type of Request 15 type RequestType int 16 17 const ( 18 // RtRun denotes a run action request 19 RtRun RequestType = 10 20 ) 21 22 // ActionWorkRequest describes a Request that Worker should handle 23 type ActionWorkRequest struct { 24 ReqType RequestType 25 ID string 26 actionData *ActionData 27 } 28 29 // ActionData action related data to pass along in a ActionWorkRequest 30 type ActionData struct { 31 context context.Context 32 action action.Action 33 inputs map[string]*data.Attribute 34 arc chan *ActionResult 35 36 options map[string]interface{} 37 } 38 39 // ActionResult is a simple struct to hold the results for an Action 40 type ActionResult struct { 41 results map[string]*data.Attribute 42 err error 43 } 44 45 // A ActionWorker handles WorkRequest, work requests consist of start, restart 46 // and resume of FlowInstances 47 type ActionWorker struct { 48 ID int 49 runner *DirectRunner 50 Work chan ActionWorkRequest 51 WorkerQueue chan chan ActionWorkRequest 52 QuitChan chan bool 53 } 54 55 // NewWorker creates, and returns a new Worker object. Its only argument 56 // is a channel that the worker can add itself to whenever it is done its 57 // work. 58 func NewWorker(id int, runner *DirectRunner, workerQueue chan chan ActionWorkRequest) ActionWorker { 59 // Create, and return the worker. 60 worker := ActionWorker{ 61 ID: id, 62 runner: runner, 63 Work: make(chan ActionWorkRequest), 64 WorkerQueue: workerQueue, 65 QuitChan: make(chan bool)} 66 67 return worker 68 } 69 70 // Start function "starts" the worker by starting a goroutine, that is 71 // an infinite "for-select" loop. This is where all the request are handled 72 func (w ActionWorker) Start() { 73 go func() { 74 for { 75 // Add ourselves into the worker queue. 76 w.WorkerQueue <- w.Work 77 78 select { 79 case work := <-w.Work: 80 // Receive a work request. 81 logger.Debugf("Action-Worker-%d: Received Request", w.ID) 82 83 switch work.ReqType { 84 default: 85 86 err := fmt.Errorf("unsupported work request type: '%d'", work.ReqType) 87 actionData := work.actionData 88 actionData.arc <- &ActionResult{err: err} 89 90 case RtRun: 91 92 actionData := work.actionData 93 94 handler := &AsyncResultHandler{result: make(chan *ActionResult), done: make(chan bool, 1)} 95 96 md := action.GetMetadata(actionData.action) 97 98 if !md.Async { 99 syncAct := actionData.action.(action.SyncAction) 100 results, err := syncAct.Run(actionData.context, actionData.inputs) 101 logger.Debugf("Action-Worker-%d: Received result: %v", w.ID, results) 102 actionData.arc <- &ActionResult{results: results, err: err} 103 } else { 104 asyncAct := actionData.action.(action.AsyncAction) 105 106 err := asyncAct.Run(actionData.context, actionData.inputs, handler) 107 108 if err != nil { 109 logger.Debugf("Action-Worker-%d: Action Run error: %s", w.ID, err.Error()) 110 // error so just return 111 actionData.arc <- &ActionResult{err: err} 112 } else { 113 done := false 114 115 replied := false 116 117 //wait for reply 118 for !done { 119 select { 120 case result := <-handler.result: 121 if !replied { 122 replied = true 123 logger.Debugf("Action-Worker-%d: Received result: %#v", w.ID, result) 124 actionData.arc <- result 125 } 126 case <-handler.done: 127 if !replied { 128 actionData.arc <- &ActionResult{} 129 } 130 done = true 131 } 132 } 133 } 134 } 135 136 logger.Debugf("Action-Worker-%d: Completed Request", w.ID) 137 } 138 139 case <-w.QuitChan: 140 // We have been asked to stop. 141 logger.Debugf("Action-Worker-%d: Stopping", w.ID) 142 return 143 } 144 } 145 }() 146 } 147 148 // Stop tells the worker to stop listening for work requests. 149 // 150 // Note that the worker will only stop *after* it has finished its work. 151 func (w ActionWorker) Stop() { 152 go func() { 153 w.QuitChan <- true 154 }() 155 } 156 157 // AsyncResultHandler simple ResultHandler to use in the asynchronous case 158 type AsyncResultHandler struct { 159 done chan bool 160 result chan *ActionResult 161 } 162 163 // HandleResult implements action.ResultHandler.HandleResult 164 func (rh *AsyncResultHandler) HandleResult(results map[string]*data.Attribute, err error) { 165 rh.result <- &ActionResult{results: results, err: err} 166 } 167 168 // Done implements action.ResultHandler.Done 169 func (rh *AsyncResultHandler) Done() { 170 rh.done <- true 171 }