github.com/insolar/vanilla@v0.0.0-20201023172447-248fdf805322/atomickit/flag_startstop.go (about) 1 // Copyright 2020 Insolar Network Ltd. 2 // All rights reserved. 3 // This material is licensed under the Insolar License version 1.0, 4 // available at https://github.com/insolar/assured-ledger/blob/master/LICENSE.md. 5 6 package atomickit 7 8 import ( 9 "runtime" 10 "sync/atomic" 11 "time" 12 ) 13 14 const ( 15 starting = -1 16 active = 1 17 stopping = 2 18 stopped = 3 19 ) 20 21 type StartStopFlag struct { 22 done int32 23 } 24 25 func (p *StartStopFlag) IsActive() bool { 26 return atomic.LoadInt32(&p.done) == active 27 } 28 29 func (p *StartStopFlag) IsStarting() bool { 30 return atomic.LoadInt32(&p.done) < 0 31 } 32 33 func (p *StartStopFlag) WasStarted() bool { 34 return atomic.LoadInt32(&p.done) != 0 35 } 36 37 func (p *StartStopFlag) WasStopped() bool { 38 return atomic.LoadInt32(&p.done) >= stopping 39 } 40 41 func (p *StartStopFlag) IsStopping() bool { 42 return atomic.LoadInt32(&p.done) == stopping 43 } 44 45 func (p *StartStopFlag) IsStopped() bool { 46 return atomic.LoadInt32(&p.done) == stopped 47 } 48 49 func (p *StartStopFlag) Status() (isActive, wasStarted bool) { 50 n := atomic.LoadInt32(&p.done) 51 return n == active, n != 0 52 } 53 54 func (p *StartStopFlag) DoStart(f func()) bool { 55 if !atomic.CompareAndSwapInt32(&p.done, 0, starting) { 56 return false 57 } 58 p.doSlow(f, active) 59 return true 60 } 61 62 func (p *StartStopFlag) DoStop(f func()) bool { 63 if !atomic.CompareAndSwapInt32(&p.done, active, stopping) { 64 return false 65 } 66 p.doSlow(f, stopped) 67 return true 68 } 69 70 func (p *StartStopFlag) DoDiscard(discardFn, stopFn func()) bool { 71 for i := uint(0); ; i++ { 72 switch atomic.LoadInt32(&p.done) { 73 case 0: 74 if atomic.CompareAndSwapInt32(&p.done, 0, stopping) { 75 p.doSlow(discardFn, stopped) 76 return true 77 } 78 case active: 79 return p.DoStop(stopFn) 80 case starting: 81 spinWait(int(i)) 82 default: 83 return false 84 } 85 } 86 } 87 88 func spinWait(spinCount int) { 89 switch { 90 case spinCount < 10: 91 runtime.Gosched() 92 case spinCount < 100: 93 time.Sleep(time.Microsecond) 94 default: 95 time.Sleep(time.Millisecond) 96 } 97 } 98 99 func (p *StartStopFlag) DoDiscardByOne(fn func(wasStarted bool)) bool { 100 if fn == nil { 101 return p.DoDiscard(nil, nil) 102 } 103 return p.DoDiscard(func() { 104 fn(false) 105 }, func() { 106 fn(true) 107 }) 108 } 109 110 func (p *StartStopFlag) Start() bool { 111 return atomic.CompareAndSwapInt32(&p.done, 0, active) 112 } 113 114 func (p *StartStopFlag) Stop() bool { 115 return atomic.CompareAndSwapInt32(&p.done, active, stopped) 116 } 117 118 func (p *StartStopFlag) doSlow(f func(), status int32) { 119 upd := int32(stopped) 120 defer func() { 121 atomic.StoreInt32(&p.done, upd) 122 }() 123 124 if f != nil { 125 f() 126 } 127 upd = status 128 }