github.com/dfklegend/cell2/utils@v0.0.0-20240402033734-a0a9f3d9335d/timer/timer.go (about)

     1  package timer
     2  
     3  import (
     4  	"sync"
     5  	"time"
     6  
     7  	"github.com/dfklegend/cell2/utils/common"
     8  	l "github.com/dfklegend/cell2/utils/logger"
     9  )
    10  
    11  /**
    12   * timer到期后,被push到channel
    13   * 便于整合到具体使用线程
    14   * 此timer性能一般,后续考虑做一个性能更好的
    15   */
    16  
    17  type IdType uint64
    18  type CBFunc func(...interface{})
    19  type QueueChan chan *Obj
    20  
    21  var timerMgr = NewTimerMgr()
    22  
    23  type ITimerMgr interface {
    24  	After(duration time.Duration, cb CBFunc, args ...interface{}) IdType
    25  	AddTimer(duration time.Duration, cb func(), args ...interface{}) IdType
    26  	Cancel(id IdType)
    27  	GetQueue() QueueChan
    28  }
    29  
    30  type Obj struct {
    31  	TimerId  IdType
    32  	Duration time.Duration
    33  	CB       CBFunc
    34  	Args     []interface{}
    35  	Canceled bool
    36  	timer    *time.Timer
    37  }
    38  
    39  // NewTimerObj TODO: 可以考虑pool
    40  func NewTimerObj(id IdType,
    41  	duration time.Duration,
    42  	cb CBFunc,
    43  	args []interface{}) *Obj {
    44  	return &Obj{
    45  		TimerId:  id,
    46  		Duration: duration,
    47  		CB:       cb,
    48  		Args:     args,
    49  		Canceled: false,
    50  	}
    51  }
    52  
    53  // Mgr 每个环境可以创建自己的timerMgr
    54  type Mgr struct {
    55  	idService *common.SerialIdService64
    56  	queue     QueueChan
    57  
    58  	// 用于cancel
    59  	// TimerIdType:*TimerObj
    60  	timers  sync.Map
    61  	running bool
    62  }
    63  
    64  func NewTimerMgr() *Mgr {
    65  	return &Mgr{
    66  		idService: common.NewSerialIdService64(),
    67  		queue:     make(QueueChan, 999),
    68  		running:   true,
    69  	}
    70  }
    71  
    72  //	用于简单测试
    73  //	用户可以创建自己的
    74  func GetTimerMgr() *Mgr {
    75  	return timerMgr
    76  }
    77  
    78  func (m *Mgr) Stop() {
    79  	m.running = false
    80  }
    81  
    82  func (m *Mgr) allocId() IdType {
    83  	return IdType(m.idService.AllocId())
    84  }
    85  
    86  func (m *Mgr) After(duration time.Duration, cb CBFunc, args ...interface{}) IdType {
    87  	t := NewTimerObj(m.allocId(),
    88  		0, cb, args)
    89  
    90  	m.doLater(duration, t)
    91  	m.timers.Store(t.TimerId, t)
    92  	return t.TimerId
    93  }
    94  
    95  func (m *Mgr) AddTimer(duration time.Duration, cb CBFunc, args ...interface{}) IdType {
    96  	t := NewTimerObj(m.allocId(),
    97  		duration, cb, args)
    98  
    99  	m.doLater(duration, t)
   100  	m.timers.Store(t.TimerId, t)
   101  	return t.TimerId
   102  }
   103  
   104  func (m *Mgr) Cancel(id IdType) {
   105  	v, ok := m.timers.Load(id)
   106  	if !ok {
   107  		return
   108  	}
   109  	t := v.(*Obj)
   110  	t.Canceled = true
   111  	if t.timer != nil {
   112  		t.timer.Stop()
   113  	}
   114  
   115  	m.timers.Delete(id)
   116  }
   117  
   118  func (m *Mgr) doLater(duration time.Duration, t *Obj) {
   119  	t.timer = time.AfterFunc(duration, func() {
   120  		//log.Println("cb")
   121  		if t.Canceled {
   122  			return
   123  		}
   124  
   125  		// 停止了
   126  		if !m.running {
   127  			return
   128  		}
   129  		// 底层在新routine执行
   130  		// time.AfterFunc
   131  		m.queue <- t
   132  	})
   133  }
   134  
   135  /*
   136  	select {
   137  		case t :<- mgr.GetQueue():
   138  			mgr.Do(t)
   139  	}
   140  */
   141  // 外部从queue读取后,调用
   142  func (m *Mgr) Do(t *Obj) {
   143  	if t.Canceled {
   144  		return
   145  	}
   146  
   147  	//t.CB(t.Args...)
   148  	m.do(t)
   149  
   150  	// CB内被cancel掉了
   151  	if t.Canceled {
   152  		return
   153  	}
   154  
   155  	if t.Duration > 0 {
   156  		m.doLater(t.Duration, t)
   157  	} else {
   158  		m.timers.Delete(t.TimerId)
   159  	}
   160  }
   161  
   162  func (m *Mgr) do(t *Obj) {
   163  	defer func() {
   164  		if err := recover(); err != nil {
   165  			l.E.Errorf("panic in timer.do:%v", err)
   166  			l.E.Errorf(common.GetStackStr())
   167  		}
   168  	}()
   169  
   170  	t.CB(t.Args...)
   171  }
   172  
   173  func (m *Mgr) GetQueue() QueueChan {
   174  	return m.queue
   175  }