Repository navigation
Expand file tree
/
Copy pathtask.go
More file actions
205 lines (178 loc) · 6.34 KB
/
Copy pathtask.go
File metadata and controls
205 lines (178 loc) · 6.34 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
package timer
import (
"sync"
"sync/atomic"
"time"
)
var _ DerefTask = (*Task)(nil)
var _ Job = (*Task)(nil)
// Task timer task.
type Task struct {
delay atomic.Int64 // delay duration.
job Schedule // the job of future execution, assign only at initialization. @Task.WithScheduleJob
rw sync.RWMutex // protects following fields.
taskEntry *taskEntry // the taskEntry to which the task belongs.
}
// MustTask returns the task if the error is nil, otherwise panics.
func MustTask(t *Task, err error) *Task {
if err != nil {
panic(err)
}
return t
}
// NewScheduleTask new task with delay duration and a schedule job.
func NewScheduleTask(d time.Duration, sj Schedule) *Task {
t := &Task{job: sj}
t.delay.Store(int64(d))
return t
}
// NewOneshotTask new task with delay duration and a job, the job will run once.
// see [timer.Oneshot]
func NewOneshotTask(d time.Duration, job Job) *Task {
return NewScheduleTask(d, Oneshot(job))
}
// NewOneshotTaskFunc new task with delay duration and a function, the function will run once.
func NewOneshotTaskFunc(d time.Duration, f func()) *Task {
return NewOneshotTask(d, JobFunc(f))
}
// NewPeriodicTask new task with delay duration and a job, the job will run periodically.
// see [timer.Periodic]
func NewPeriodicTask(d time.Duration, job Job) *Task {
j := Periodic(d, job)
return NewScheduleTask(j.NextDelay(), j)
}
// NewPeriodicTaskFunc new task with delay duration and a function, the job will run periodically.
func NewPeriodicTaskFunc(d time.Duration, f func()) *Task {
return NewPeriodicTask(d, JobFunc(f))
}
// NewCrontabTask new task with crontab spec and a job, the job will run periodically.
func NewCrontabTask(spec string, job Job) (*Task, error) {
j, err := Crontab(spec, job)
if err != nil {
return nil, err
}
return NewScheduleTask(j.NextDelay(), j), nil
}
// NewCrontabTaskFunc new task with crontab spec and a function, the job will run periodically.
func NewCrontabTaskFunc(spec string, f func()) (*Task, error) {
return NewCrontabTask(spec, JobFunc(f))
}
// NewTask new task with delay duration and an empty job, the accuracy is milliseconds.
func NewTask(d time.Duration) *Task {
return NewScheduleTask(d, emptyJob)
}
// NewTaskJob new task with delay duration and a job, the accuracy is milliseconds.
//
// Deprecated: this value is simply [timer.NewOneshotTask].
func NewTaskJob(d time.Duration, job Job) *Task {
return NewOneshotTask(d, job)
}
// NewTaskFunc new task with delay duration and a function job, the accuracy is milliseconds.
//
// Deprecated: this value is simply [timer.NewOneshotTaskFunc].
func NewTaskFunc(d time.Duration, f func()) *Task {
return NewOneshotTaskFunc(d, f)
}
// WithJobFunc with a function, the function will be wrapped in an OneshotJob.
//
// Deprecated: this value is simply [timer.WithScheduleJob(NewOneshotJob(JobFunc(f)))].
func (t *Task) WithJobFunc(f func()) *Task {
return t.WithScheduleJob(Oneshot(JobFunc(f)))
}
// WithJob with a job, the job will be wrapped in an OneshotJob.
//
// Deprecated: this value is simply [timer.WithScheduleJob(NewOneshotJob(j))].
func (t *Task) WithJob(j Job) *Task {
return t.WithScheduleJob(Oneshot(j))
}
// WithScheduleJob with a schedule job.
//
// NOTE: it is NOT safe for concurrent use, assign the job only at initialization,
// before the task is added to a `Timer`. Once added, the job is read from the
// goroutine pool (@Task.Run, @Timer.addTaskEntry), so assigning it afterwards is a
// data race. `Task.job` therefore needs no lock. To change the job, use a new task.
func (t *Task) WithScheduleJob(j Schedule) *Task {
t.job = j
return t
}
// DerefTask implements TaskContainer.
func (t *Task) DerefTask() *Task { return t }
// Run immediate call job. implement Job interface.
func (t *Task) Run() {
t.job.Run()
}
// Cancel the task.
//
// It unlinks the task from the wheel. For a oneshot task (@timer.Oneshot) that is
// all there is: it stops firing. For a recurring task (@timer.Periodic,
// @timer.Crontab) it stops the schedule for good too: the re-schedule path
// (@Timer.addTaskEntry) checks the task still owns its entry before re-adding, so a
// cancelled task is never re-inserted after the hand-off. The one caveat: a cycle
// already handed off to the goroutine pool may still run once, but it will not
// re-schedule. To stop a recurring task by schedule instead, have its
// `Schedule.NextDelay` report a value <= 0, or stop the whole `Timer`.
func (t *Task) Cancel() {
t.rw.Lock()
defer t.rw.Unlock()
if t.taskEntry != nil {
t.taskEntry.remove()
t.taskEntry = nil
}
}
// Delay return the delay duration.
func (t *Task) Delay() time.Duration {
return time.Duration(t.delay.Load())
}
// SetDelay set a new delay duration, the accuracy is milliseconds.
// It must be greater than 0, otherwise `Timer.AddTask` reports `ErrInvalidDelay`.
// NOTE: Only effect when re-add to `Timer`, It has no effect on the task being running!
func (t *Task) SetDelay(d time.Duration) *Task {
t.delay.Store(int64(d))
return t
}
// Activated return true if the task is activated.
func (t *Task) Activated() bool {
t.rw.RLock()
defer t.rw.RUnlock()
// why need check task entry?
// when cancel, we will set t.taskEntry to nil,
// but if the task is expired, only remove the task entry from the spoke.
// so we should check the task entry..
return t.taskEntry != nil && t.taskEntry.activated()
}
// Expiry return the milliseconds as a Unix time when the task will be expired.
// the number of milliseconds elapsed since January 1, 1970 UTC.
// the value -1 indicate the task not activated.
func (t *Task) Expiry() int64 {
t.rw.RLock()
defer t.rw.RUnlock()
if t.taskEntry != nil && t.taskEntry.activated() {
return t.taskEntry.ExpirationMs()
}
return -1
}
// ExpiryAt return the local time when the task will be expired.
// the zero time indicate the task not activated.
func (t *Task) ExpiryAt() time.Time {
if ms := t.Expiry(); ms < 0 {
return time.Time{}
} else {
return time.UnixMilli(ms)
}
}
// setBelongTo set the task belongs to the task entry.
func (t *Task) setBelongTo(te *taskEntry) {
t.rw.Lock()
defer t.rw.Unlock()
// if this task already belong to an existing task entry,
// we should remove such an entry first.
if t.taskEntry != nil && t.taskEntry != te {
t.taskEntry.remove()
}
t.taskEntry = te
}
func (t *Task) isBelongTo(te *taskEntry) bool {
t.rw.RLock()
defer t.rw.RUnlock()
return t.taskEntry == te
}