@@ -44,28 +44,30 @@ func (list *List) consumer() {
4444 for {
4545 select {
4646 case task := <- list .queue :
47+ // Set task state to RUNNING before processing
4748 list .Lock ()
48- {
49- task .State = RUNNING
50- }
49+ task .State = RUNNING
5150 list .Unlock ()
5251
5352 go func () {
5453 retValue , err := task .process (aptly .Progress (task .output ), task .detail )
5554
55+ // Update task completion state and cleanup with list lock held
5656 list .Lock ()
5757 {
58- task .processReturnValue = retValue
59- task .err = err
6058 if err != nil {
6159 task .output .Printf ("Task failed with error: %v" , err )
6260 task .State = FAILED
61+ task .err = err
62+ task .processReturnValue = retValue
6363 } else {
6464 task .output .Print ("Task succeeded" )
6565 task .State = SUCCEEDED
66+ task .err = nil
67+ task .processReturnValue = retValue
6668 }
6769
68- list .usedResources .Free (task .resources )
70+ list .usedResources .Free (task .Resources )
6971
7072 task .wgTask .Done ()
7173 list .wg .Done ()
@@ -74,9 +76,9 @@ func (list *List) consumer() {
7476 for _ , t := range list .tasks {
7577 if t .State == IDLE {
7678 // check resources
77- blockingTasks := list .usedResources .UsedBy (t .resources )
79+ blockingTasks := list .usedResources .UsedBy (t .Resources )
7880 if len (blockingTasks ) == 0 {
79- list .usedResources .MarkInUse (t .resources , t )
81+ list .usedResources .MarkInUse (t .Resources , t )
8082 // unlock list since queueing may block
8183 list .Unlock ()
8284 unlocked = true
@@ -105,13 +107,15 @@ func (list *List) Stop() {
105107
106108// GetTasks gets complete list of tasks
107109func (list * List ) GetTasks () []Task {
108- tasks := []Task {}
109110 list .Lock ()
111+ defer list .Unlock ()
112+
113+ tasks := []Task {}
110114 for _ , task := range list .tasks {
115+ // Copy task while holding list lock
111116 tasks = append (tasks , * task )
112117 }
113118
114- list .Unlock ()
115119 return tasks
116120}
117121
@@ -139,11 +143,11 @@ func (list *List) DeleteTaskByID(ID int) (Task, error) {
139143// GetTaskByID returns task with given id
140144func (list * List ) GetTaskByID (ID int ) (Task , error ) {
141145 list .Lock ()
142- tasks := list .tasks
143- list .Unlock ()
146+ defer list .Unlock ()
144147
145- for _ , task := range tasks {
148+ for _ , task := range list . tasks {
146149 if task .ID == ID {
150+ // Copy task while holding list lock
147151 return * task , nil
148152 }
149153 }
@@ -180,13 +184,16 @@ func (list *List) GetTaskDetailByID(ID int) (interface{}, error) {
180184
181185// GetTaskReturnValueByID returns process return value of task with given id
182186func (list * List ) GetTaskReturnValueByID (ID int ) (* ProcessReturnValue , error ) {
183- task , err := list .GetTaskByID (ID )
187+ list .Lock ()
188+ defer list .Unlock ()
184189
185- if err != nil {
186- return nil , err
190+ for _ , task := range list .tasks {
191+ if task .ID == ID {
192+ return task .processReturnValue , nil
193+ }
187194 }
188195
189- return task . processReturnValue , nil
196+ return nil , fmt . Errorf ( "could not find task with id %v" , ID )
190197}
191198
192199// RunTaskInBackground creates task and runs it in background. This will block until the necessary resources
@@ -204,24 +211,29 @@ func (list *List) RunTaskInBackground(name string, resources []string, process P
204211 list .wg .Add (1 )
205212 task .wgTask .Add (1 )
206213
214+ // Copy task while still holding the lock to avoid racing with consumer
215+ // setting State=RUNNING after receiving from queue
216+ taskCopy := * task
217+
207218 // add task to queue for processing if resources are available
208219 // if not, task will be queued by the consumer once resources are available
209220 tasks := list .usedResources .UsedBy (resources )
210221 if len (tasks ) == 0 {
211- list .usedResources .MarkInUse (task .resources , task )
222+ list .usedResources .MarkInUse (task .Resources , task )
212223 // queueing task might block if channel not ready, unlock list before queueing
213224 list .Unlock ()
214225 list .queue <- task
215226 } else {
216227 list .Unlock ()
217228 }
218229
219- return * task , nil
230+ return taskCopy , nil
220231}
221232
222233// Clear removes finished tasks from list
223234func (list * List ) Clear () {
224235 list .Lock ()
236+ defer list .Unlock ()
225237
226238 var tasks []* Task
227239 for _ , task := range list .tasks {
@@ -230,8 +242,6 @@ func (list *List) Clear() {
230242 }
231243 }
232244 list .tasks = tasks
233-
234- list .Unlock ()
235245}
236246
237247// Wait waits till all tasks are processed
@@ -254,11 +264,14 @@ func (list *List) WaitForTaskByID(ID int) (Task, error) {
254264
255265// GetTaskErrorByID returns the Task error for a given id
256266func (list * List ) GetTaskErrorByID (ID int ) (error , error ) {
257- task , err := list .GetTaskByID (ID )
267+ list .Lock ()
268+ defer list .Unlock ()
258269
259- if err != nil {
260- return nil , err
270+ for _ , task := range list .tasks {
271+ if task .ID == ID {
272+ return task .err , nil
273+ }
261274 }
262275
263- return task . err , nil
276+ return nil , fmt . Errorf ( "could not find task with id %v" , ID )
264277}
0 commit comments