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 }