gitee.com/h79/goutils@v1.22.10/common/system/run.go (about)

     1  package system
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"gitee.com/h79/goutils/common/option"
     8  	"os"
     9  	"os/signal"
    10  	"sync"
    11  	"sync/atomic"
    12  	"syscall"
    13  	"time"
    14  )
    15  
    16  var closeCh chan bool
    17  var quited bool
    18  var quitErr error
    19  var quitCode int32
    20  var ErrNormalQuit = errors.New("normal quit")
    21  
    22  func init() {
    23  	var _ = closeCh
    24  	var _ = quited
    25  	closeCh = make(chan bool)
    26  	quited = false
    27  }
    28  
    29  type ExitSig struct {
    30  	S chan os.Signal
    31  }
    32  
    33  func (c *ExitSig) Done() <-chan os.Signal {
    34  	return c.S
    35  }
    36  
    37  func Exit() *ExitSig {
    38  	e := &ExitSig{S: make(chan os.Signal)}
    39  	signal.Notify(e.S, os.Interrupt, os.Kill, syscall.SIGTERM, syscall.SIGQUIT)
    40  	return e
    41  }
    42  
    43  func IsNormalQuit() bool {
    44  	return quited && (quitErr == nil || errors.Is(ErrNormalQuit, quitErr))
    45  }
    46  
    47  func IsQuit() bool {
    48  	return quited
    49  }
    50  
    51  func GetQuitCode() int32 {
    52  	return quitCode
    53  }
    54  
    55  func SetQuitCode(code int32) {
    56  	quitCode = code
    57  }
    58  
    59  func GetQuitErr() error {
    60  	return quitErr
    61  }
    62  
    63  func SetQuitErr(err error) {
    64  	quitErr = err
    65  }
    66  
    67  func QuitApp(code int32) {
    68  	if quited {
    69  		return
    70  	}
    71  	quited = true
    72  	quitCode = code
    73  	close(closeCh)
    74  	fmt.Println("quit program.")
    75  }
    76  
    77  func Close() {
    78  	QuitApp(-1)
    79  }
    80  
    81  func Closed() <-chan bool {
    82  	return closeCh
    83  }
    84  
    85  func AfterQuit(t time.Duration) {
    86  	Wait(t)
    87  }
    88  
    89  func Wait(t time.Duration) {
    90  	ti := time.NewTimer(t)
    91  	defer ti.Stop()
    92  	select {
    93  	case <-ti.C:
    94  		return
    95  	}
    96  }
    97  
    98  func Stop(d time.Duration, stop chan bool) {
    99  	go func() {
   100  		Wait(d)
   101  		ti := time.NewTimer(d * 2)
   102  		defer ti.Stop()
   103  		select {
   104  		case <-stop:
   105  			stop <- true
   106  			return
   107  		case <-ti.C:
   108  			return
   109  		}
   110  	}()
   111  	stop <- true
   112  	<-stop
   113  }
   114  
   115  type Running struct {
   116  	ev *Event
   117  	c  int32
   118  	wg sync.WaitGroup
   119  }
   120  
   121  var (
   122  	mainIf  int32 = 0
   123  	running *Running
   124  	once    sync.Once
   125  )
   126  
   127  func Go() *Running {
   128  	once.Do(func() {
   129  		running = NewRunning()
   130  	})
   131  	return running
   132  }
   133  
   134  // NewRunning
   135  // f 是否fire
   136  func NewRunning() *Running {
   137  	return &Running{ev: NewEvent()}
   138  }
   139  
   140  func ChildRunning(fn func(), exits ...func()) {
   141  	Go().Run(false, fn, exits...)
   142  }
   143  
   144  func MainRunning(fn func(), exits ...func()) {
   145  	if atomic.AddInt32(&mainIf, 1) > 1 {
   146  		panic("can not repeat call,only call one")
   147  	}
   148  	Go().runM(fn, exits...)
   149  }
   150  
   151  // Run if fire=true, select Running.Done()
   152  func (m *Running) Run(fire bool, fn func(), exits ...func()) {
   153  	if quited {
   154  		return
   155  	}
   156  	var c = atomic.AddInt32(&m.c, 1)
   157  	fmt.Println("enter child go coroutine,count: ", c)
   158  	m.wg.Add(1)
   159  	go func() {
   160  		defer m.done(true, fire)
   161  		defer Recover(exits...)
   162  		fn()
   163  	}()
   164  }
   165  
   166  func (m *Running) runM(fn func(), exits ...func()) {
   167  	if quited {
   168  		return
   169  	}
   170  	var c = atomic.AddInt32(&m.c, 1)
   171  	fmt.Println("enter main go coroutine,count: ", c)
   172  	m.wg.Add(1)
   173  	go func() {
   174  		defer m.done(false, true)
   175  		defer Recover(exits...)
   176  		defer Close()
   177  		defer atomic.StoreInt32(&mainIf, 0)
   178  		fn()
   179  	}()
   180  }
   181  
   182  func (m *Running) done(child, fire bool) {
   183  	var c = atomic.AddInt32(&m.c, -1)
   184  	fmt.Println("exit go coroutine, count: ", c, ",it is main coroutine: ", !child)
   185  	if fire {
   186  		m.ev.Fire()
   187  	}
   188  	m.wg.Done()
   189  }
   190  
   191  func (m *Running) Done() <-chan struct{} {
   192  	return m.ev.Done()
   193  }
   194  
   195  func (m *Running) Wait() {
   196  	m.wg.Wait()
   197  }
   198  
   199  func (m *Running) Count() int32 {
   200  	return atomic.LoadInt32(&m.c)
   201  }
   202  
   203  type RunningCheck struct {
   204  	rm   sync.RWMutex
   205  	fRun bool
   206  }
   207  
   208  func (rc *RunningCheck) IsRunning() bool {
   209  	rc.rm.RLock()
   210  	defer rc.rm.RUnlock()
   211  	return rc.fRun
   212  }
   213  
   214  // Deprecated: this function simply calls [GoRunning].
   215  func (rc *RunningCheck) TryGoRunning(fn func(), exits ...func()) {
   216  	rc.GoRunning(fn, exits...)
   217  }
   218  
   219  func (rc *RunningCheck) GoRunning(fn func(), exits ...func()) {
   220  	rc.GoRunningV2(func() {}, fn, exits...)
   221  }
   222  
   223  func (rc *RunningCheck) GoRunningV2(running func(), fn func(), exits ...func()) {
   224  	if rc.IsRunning() {
   225  		return
   226  	}
   227  	rc.runningSet(true)
   228  	running()
   229  	ChildRunning(func() {
   230  		defer rc.runningSet(false)
   231  		fn()
   232  	}, exits...)
   233  }
   234  
   235  func (rc *RunningCheck) runningSet(flag bool) {
   236  	rc.rm.Lock()
   237  	rc.fRun = flag
   238  	rc.rm.Unlock()
   239  }
   240  
   241  type Task func(opts ...option.Option) (any, error)
   242  
   243  func RunAfter(timeout time.Duration, task Task, opts ...option.Option) (any, error) {
   244  	if timeout <= 0 {
   245  		return task(opts...)
   246  	}
   247  	resultCh := make(chan *resultWithError, 1)
   248  
   249  	ChildRunning(func() {
   250  		result, err := task(opts...)
   251  		resultCh <- &resultWithError{result, err}
   252  	})
   253  	ti := time.NewTimer(timeout)
   254  	defer ti.Stop()
   255  	select {
   256  	case <-ti.C:
   257  		return nil, TimeoutError
   258  	case rwe := <-resultCh:
   259  		return rwe.result, rwe.err
   260  	case <-Closed():
   261  		return nil, ClosedError
   262  	}
   263  }
   264  
   265  func RunWithContext(ctx context.Context, task Task) (any, error) {
   266  	resultCh := make(chan *resultWithError, 1)
   267  	ChildRunning(func() {
   268  		result, err := task()
   269  		resultCh <- &resultWithError{result, err}
   270  	})
   271  	select {
   272  	case <-ctx.Done():
   273  		return nil, ctx.Err()
   274  	case rwe := <-resultCh:
   275  		return rwe.result, rwe.err
   276  	case <-Closed():
   277  		return nil, ClosedError
   278  	}
   279  }
   280  
   281  // Ticker
   282  // 定时调用
   283  func Ticker(tick time.Duration, fun func(any) bool, param any, funcDefer func(any) bool, paramDefer any) {
   284  	Delay(0, tick, fun, param, funcDefer, paramDefer)
   285  }
   286  
   287  func DelayOnly(delay time.Duration, fun func(any) bool, param any, funcDefer func(any) bool, paramDefer any) {
   288  	Delay(delay, 0, fun, param, funcDefer, paramDefer)
   289  }
   290  
   291  func Delay(delay, tick time.Duration, fun func(any) bool, param any, funcDefer func(any) bool, paramDefer any) {
   292  	if fun == nil {
   293  		panic("fun is nil")
   294  	}
   295  	ChildRunning(func() {
   296  		pd := ParamDefer{P: paramDefer}
   297  		defer func() {
   298  			if funcDefer != nil {
   299  				funcDefer(pd)
   300  			}
   301  		}()
   302  		if delay > 0 {
   303  			Wait(delay)
   304  		}
   305  		if tick <= 0 {
   306  			fun(param)
   307  			pd.Reason = 2
   308  			return
   309  		}
   310  		if delay > 0 {
   311  			if fun(param) {
   312  				pd.Reason = 2
   313  				return
   314  			}
   315  		}
   316  		ticker := time.NewTicker(tick)
   317  		defer ticker.Stop()
   318  		for {
   319  			select {
   320  			case <-ticker.C:
   321  				if fun(param) {
   322  					pd.Reason = 2
   323  					return
   324  				}
   325  			case <-Closed():
   326  				pd.Reason = 1
   327  				return
   328  			}
   329  		}
   330  	})
   331  }
   332  
   333  type resultWithError struct {
   334  	result any
   335  	err    error
   336  }
   337  
   338  type ParamDefer struct {
   339  	P      any
   340  	Reason int
   341  }