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  }