github.com/HashDataInc/packer@v1.3.2/helper/multistep/basic_runner.go (about) 1 package multistep 2 3 import ( 4 "context" 5 "sync" 6 "sync/atomic" 7 ) 8 9 type runState int32 10 11 const ( 12 stateIdle runState = iota 13 stateRunning 14 stateCancelling 15 ) 16 17 // BasicRunner is a Runner that just runs the given slice of steps. 18 type BasicRunner struct { 19 // Steps is a slice of steps to run. Once set, this should _not_ be 20 // modified. 21 Steps []Step 22 23 cancel context.CancelFunc 24 doneCh chan struct{} 25 state runState 26 l sync.Mutex 27 } 28 29 func (b *BasicRunner) Run(state StateBag) { 30 ctx, cancel := context.WithCancel(context.Background()) 31 32 b.l.Lock() 33 if b.state != stateIdle { 34 panic("already running") 35 } 36 37 doneCh := make(chan struct{}) 38 b.cancel = cancel 39 b.doneCh = doneCh 40 b.state = stateRunning 41 b.l.Unlock() 42 43 defer func() { 44 b.l.Lock() 45 b.cancel = nil 46 b.doneCh = nil 47 b.state = stateIdle 48 close(doneCh) 49 b.l.Unlock() 50 }() 51 52 // This goroutine listens for cancels and puts the StateCancelled key 53 // as quickly as possible into the state bag to mark it. 54 go func() { 55 select { 56 case <-ctx.Done(): 57 // Flag cancel and wait for finish 58 state.Put(StateCancelled, true) 59 <-doneCh 60 case <-doneCh: 61 } 62 }() 63 64 for _, step := range b.Steps { 65 // We also check for cancellation here since we can't be sure 66 // the goroutine that is running to set it actually ran. 67 if runState(atomic.LoadInt32((*int32)(&b.state))) == stateCancelling { 68 state.Put(StateCancelled, true) 69 break 70 } 71 72 action := step.Run(ctx, state) 73 defer step.Cleanup(state) 74 75 if _, ok := state.GetOk(StateCancelled); ok { 76 break 77 } 78 79 if action == ActionHalt { 80 state.Put(StateHalted, true) 81 break 82 } 83 } 84 } 85 86 func (b *BasicRunner) Cancel() { 87 b.l.Lock() 88 switch b.state { 89 case stateIdle: 90 // Not running, so Cancel is... done. 91 b.l.Unlock() 92 return 93 case stateRunning: 94 // Running, so mark that we cancelled and set the state 95 b.cancel() 96 b.state = stateCancelling 97 fallthrough 98 case stateCancelling: 99 // Already cancelling, so just wait until we're done 100 ch := b.doneCh 101 b.l.Unlock() 102 <-ch 103 } 104 }