github.com/benz9527/toy-box/algo@v0.0.0-20240221120937-66c0c6bd5abd/timer/x_task.go (about)

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