mirror of
https://github.com/aptly-dev/aptly.git
synced 2026-06-08 05:50:47 +00:00
4445839adc
Race condition iexisted where task State, err, and processReturnValue fields
were written by consumer goroutine and read by concurrent accessors without
proper synchronization, causing torn reads and data races.
Implemented single-lock model with optimal lock scope:
- Removed 8 accessor methods (direct field access is simpler)
- Lock only during brief state transitions (IDLE→RUNNING, RUNNING→SUCCEEDED/FAILED)
- Release lock during task.process() execution to enable full concurrency
- Readers hold list.Lock() only during atomic struct copy
- Moved State = RUNNING before goroutine spawn for clearer semantics
- task/list.go: RunTaskInBackground() copies *task before unlock,
returns the pre-made copy instead of dereferencing after unlock
73 lines
1.5 KiB
Go
73 lines
1.5 KiB
Go
package task
|
|
|
|
import (
|
|
"sync"
|
|
"sync/atomic"
|
|
|
|
"github.com/aptly-dev/aptly/aptly"
|
|
)
|
|
|
|
// State task is in
|
|
type State int
|
|
|
|
// Detail represents custom task details
|
|
type Detail struct {
|
|
atomic.Value
|
|
}
|
|
|
|
// PublishDetail represents publish task details
|
|
type PublishDetail struct {
|
|
*Detail
|
|
TotalNumberOfPackages int64
|
|
RemainingNumberOfPackages int64
|
|
}
|
|
|
|
type ProcessReturnValue struct {
|
|
Code int
|
|
Value interface{}
|
|
}
|
|
|
|
// Process is a function implementing the actual task logic
|
|
type Process func(out aptly.Progress, detail *Detail) (*ProcessReturnValue, error)
|
|
|
|
const (
|
|
// IDLE when task is waiting
|
|
IDLE State = iota
|
|
// RUNNING when task is running
|
|
RUNNING
|
|
// SUCCEEDED when task is successfully finished
|
|
SUCCEEDED
|
|
// FAILED when task failed
|
|
FAILED
|
|
)
|
|
|
|
// Task represents as task in a queue encapsulates process code
|
|
// All fields are protected by List.Mutex - access task fields only while holding list.Lock()
|
|
type Task struct {
|
|
output *Output
|
|
detail *Detail
|
|
process Process
|
|
processReturnValue *ProcessReturnValue
|
|
err error
|
|
Name string
|
|
ID int
|
|
State State
|
|
resources []string
|
|
wgTask *sync.WaitGroup
|
|
}
|
|
|
|
// NewTask creates new task
|
|
func NewTask(process Process, name string, ID int, resources []string, wgTask *sync.WaitGroup) *Task {
|
|
task := &Task{
|
|
output: NewOutput(),
|
|
detail: &Detail{},
|
|
process: process,
|
|
Name: name,
|
|
ID: ID,
|
|
State: IDLE,
|
|
resources: resources,
|
|
wgTask: wgTask,
|
|
}
|
|
return task
|
|
}
|