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 }