github.com/benz9527/xboot@v0.0.0-20240504061247-c23f15593274/timer/x_task.go (about)

     1  package timer
     2  
     3  import (
     4  	"context"
     5  	"sync/atomic"
     6  	"unsafe"
     7  
     8  	"github.com/benz9527/xboot/lib/list"
     9  )
    10  
    11  type elementTasker interface {
    12  	getAndReleaseElementRef() *list.NodeElement[Task]
    13  }
    14  
    15  type jobMetadata struct {
    16  	jobID        JobID
    17  	job          Job
    18  	expirationMs int64
    19  	loopCount    int64
    20  	jobType      JobType
    21  }
    22  
    23  func (m *jobMetadata) GetJobID() JobID {
    24  	return m.jobID
    25  }
    26  
    27  func (m *jobMetadata) GetExpiredMs() int64 {
    28  	return m.expirationMs
    29  }
    30  
    31  func (m *jobMetadata) GetRestLoopCount() int64 {
    32  	return m.loopCount
    33  }
    34  
    35  func (m *jobMetadata) GetJobType() JobType {
    36  	return m.jobType
    37  }
    38  
    39  type task struct {
    40  	*jobMetadata
    41  	slotMetadata TimingWheelSlotMetadata
    42  	slot         unsafe.Pointer // TimingWheelSlot
    43  	// Doubly pointer reference, it is easy for us to access the element in the list.
    44  	elementRef unsafe.Pointer // list.NodeElement[Task]
    45  	cancelled  *atomic.Bool
    46  }
    47  
    48  var (
    49  	_ Task          = (*task)(nil)
    50  	_ elementTasker = (*task)(nil)
    51  )
    52  
    53  func (t *task) getAndReleaseElementRef() *list.NodeElement[Task] {
    54  	ref := t.getElementRef()
    55  	t.setElementRef(nil)
    56  	return ref
    57  }
    58  
    59  func (t *task) GetJobID() JobID {
    60  	return t.jobID
    61  }
    62  
    63  func (t *task) GetJobMetadata() JobMetadata {
    64  	md := &jobMetadata{
    65  		jobID:        t.jobID,
    66  		job:          t.job,
    67  		expirationMs: t.expirationMs,
    68  		loopCount:    t.loopCount,
    69  		jobType:      t.jobType,
    70  	}
    71  	return md
    72  }
    73  
    74  func (t *task) GetRestLoopCount() int64 {
    75  	return atomic.LoadInt64(&t.loopCount)
    76  }
    77  
    78  func (t *task) GetJob() Job {
    79  	return t.job
    80  }
    81  
    82  func (t *task) Cancelled() bool {
    83  	return t.cancelled.Load()
    84  }
    85  
    86  func (t *task) GetExpiredMs() int64 {
    87  	return atomic.LoadInt64(&t.expirationMs)
    88  }
    89  
    90  func (t *task) GetSlot() TimingWheelSlot {
    91  	return *(*TimingWheelSlot)(atomic.LoadPointer(&t.slot))
    92  }
    93  
    94  func (t *task) setSlot(slot TimingWheelSlot) {
    95  	atomic.StorePointer(&t.slot, unsafe.Pointer(&slot))
    96  }
    97  
    98  func (t *task) GetPreviousSlotMetadata() TimingWheelSlotMetadata {
    99  	return t.slotMetadata
   100  }
   101  
   102  func (t *task) setSlotMetadata(slotMetadata TimingWheelSlotMetadata) {
   103  	t.slotMetadata = slotMetadata
   104  }
   105  
   106  func (t *task) getElementRef() *list.NodeElement[Task] {
   107  	return (*list.NodeElement[Task])(atomic.LoadPointer(&t.elementRef))
   108  }
   109  
   110  func (t *task) setElementRef(elementRef *list.NodeElement[Task]) {
   111  	atomic.StorePointer(&t.elementRef, unsafe.Pointer(elementRef))
   112  }
   113  
   114  func (t *task) GetJobType() JobType {
   115  	return t.jobType
   116  }
   117  
   118  func (t *task) Cancel() bool {
   119  	if stopped := t.cancelled.Swap(true); stopped {
   120  		// Previous value is true, it means that the task has been cancelled.
   121  		return true
   122  	}
   123  
   124  	// If task is cancelled, it will be removed from the timing wheel automatically
   125  	// in other process, so we don't need to remove it here.
   126  
   127  	if t.jobType == OnceJob {
   128  		atomic.SwapInt64(&t.loopCount, 0)
   129  	} else if t.jobType == RepeatedJob {
   130  		atomic.SwapInt64(&t.loopCount, t.GetRestLoopCount()-1)
   131  	}
   132  	return true
   133  }
   134  
   135  type xTask struct {
   136  	*task
   137  	ctx context.Context
   138  }
   139  
   140  func (t *xTask) getAndReleaseElementRef() *list.NodeElement[Task] {
   141  	return t.task.getAndReleaseElementRef()
   142  }
   143  
   144  func (t *xTask) Cancel() bool {
   145  	return t.task.Cancel()
   146  }
   147  
   148  type xScheduledTask struct {
   149  	*xTask
   150  	beginMs   int64
   151  	scheduler Scheduler
   152  }
   153  
   154  func (t *xScheduledTask) UpdateNextScheduledMs() {
   155  	expiredMs := t.scheduler.next(t.beginMs)
   156  	atomic.StoreInt64(&t.expirationMs, expiredMs)
   157  	if expiredMs == -1 {
   158  		return
   159  	}
   160  	atomic.SwapInt64(&t.beginMs, expiredMs)
   161  }
   162  
   163  func (t *xScheduledTask) GetRestLoopCount() int64 {
   164  	return t.scheduler.GetRestLoopCount()
   165  }
   166  
   167  func (t *xScheduledTask) getAndReleaseElementRef() *list.NodeElement[Task] {
   168  	return t.task.getAndReleaseElementRef()
   169  }
   170  
   171  func (t *xScheduledTask) Cancel() bool {
   172  	return t.task.Cancel()
   173  }
   174  
   175  var (
   176  	_ Task          = (*task)(nil)
   177  	_ Task          = (*xTask)(nil)
   178  	_ ScheduledTask = (*xScheduledTask)(nil)
   179  )
   180  
   181  func NewOnceTask(
   182  	ctx context.Context,
   183  	jobID JobID,
   184  	expiredMs int64,
   185  	job Job,
   186  ) Task {
   187  	if ctx == nil {
   188  		return nil
   189  	}
   190  
   191  	t := &xTask{
   192  		task: &task{
   193  			jobMetadata: &jobMetadata{
   194  				jobID:        jobID,
   195  				expirationMs: expiredMs,
   196  				loopCount:    1,
   197  				job:          job,
   198  				jobType:      OnceJob,
   199  			},
   200  			cancelled: &atomic.Bool{},
   201  		},
   202  		ctx: ctx,
   203  	}
   204  	return t
   205  }
   206  
   207  func NewRepeatTask(
   208  	ctx context.Context,
   209  	jobID JobID,
   210  	beginMs int64,
   211  	scheduler Scheduler,
   212  	job Job,
   213  ) ScheduledTask {
   214  	if ctx == nil || scheduler == nil || job == nil {
   215  		return nil
   216  	}
   217  	t := &xScheduledTask{
   218  		xTask: &xTask{
   219  			task: &task{
   220  				jobMetadata: &jobMetadata{
   221  					jobID:   jobID,
   222  					job:     job,
   223  					jobType: RepeatedJob,
   224  				},
   225  				cancelled: &atomic.Bool{},
   226  			},
   227  			ctx: ctx,
   228  		},
   229  		scheduler: scheduler,
   230  		beginMs:   beginMs,
   231  	}
   232  	t.UpdateNextScheduledMs()
   233  	return t
   234  }