3
3
package cron
4
4
5
5
import (
6
+ "log"
7
+ "runtime"
6
8
"sort"
7
9
"time"
8
10
)
@@ -16,6 +18,8 @@ type Cron struct {
16
18
add chan * Entry
17
19
snapshot chan []* Entry
18
20
running bool
21
+ ErrorLog * log.Logger
22
+ location * time.Location
19
23
}
20
24
21
25
// Job is an interface for submitted cron jobs.
@@ -66,14 +70,21 @@ func (s byTime) Less(i, j int) bool {
66
70
return s [i ].Next .Before (s [j ].Next )
67
71
}
68
72
69
- // New returns a new Cron job runner.
73
+ // New returns a new Cron job runner, in the Local time zone .
70
74
func New () * Cron {
75
+ return NewWithLocation (time .Now ().Location ())
76
+ }
77
+
78
+ // NewWithLocation returns a new Cron job runner.
79
+ func NewWithLocation (location * time.Location ) * Cron {
71
80
return & Cron {
72
81
entries : nil ,
73
82
add : make (chan * Entry ),
74
83
stop : make (chan struct {}),
75
84
snapshot : make (chan []* Entry ),
76
85
running : false ,
86
+ ErrorLog : nil ,
87
+ location : location ,
77
88
}
78
89
}
79
90
@@ -87,7 +98,7 @@ func (c *Cron) AddFunc(spec string, cmd func()) error {
87
98
return c .AddJob (spec , FuncJob (cmd ))
88
99
}
89
100
90
- // AddFunc adds a Job to the Cron to be run on the given schedule.
101
+ // AddJob adds a Job to the Cron to be run on the given schedule.
91
102
func (c * Cron ) AddJob (spec string , cmd Job ) error {
92
103
schedule , err := Parse (spec )
93
104
if err != nil {
@@ -121,17 +132,37 @@ func (c *Cron) Entries() []*Entry {
121
132
return c .entrySnapshot ()
122
133
}
123
134
124
- // Start the cron scheduler in its own go-routine.
135
+ // Location gets the time zone location
136
+ func (c * Cron ) Location () * time.Location {
137
+ return c .location
138
+ }
139
+
140
+ // Start the cron scheduler in its own go-routine, or no-op if already started.
125
141
func (c * Cron ) Start () {
142
+ if c .running {
143
+ return
144
+ }
126
145
c .running = true
127
146
go c .run ()
128
147
}
129
148
149
+ func (c * Cron ) runWithRecovery (j Job ) {
150
+ defer func () {
151
+ if r := recover (); r != nil {
152
+ const size = 64 << 10
153
+ buf := make ([]byte , size )
154
+ buf = buf [:runtime .Stack (buf , false )]
155
+ c .logf ("cron: panic running job: %v\n %s" , r , buf )
156
+ }
157
+ }()
158
+ j .Run ()
159
+ }
160
+
130
161
// Run the scheduler.. this is private just due to the need to synchronize
131
162
// access to the 'running' state variable.
132
163
func (c * Cron ) run () {
133
164
// Figure out the next activation times for each entry.
134
- now := time .Now ().Local ( )
165
+ now := time .Now ().In ( c . location )
135
166
for _ , entry := range c .entries {
136
167
entry .Next = entry .Schedule .Next (now )
137
168
}
@@ -149,32 +180,44 @@ func (c *Cron) run() {
149
180
effective = c .entries [0 ].Next
150
181
}
151
182
183
+ timer := time .NewTimer (effective .Sub (now ))
152
184
select {
153
- case now = <- time . After ( effective . Sub ( now )) :
185
+ case now = <- timer . C :
154
186
// Run every entry whose next time was this effective time.
155
187
for _ , e := range c .entries {
156
188
if e .Next != effective {
157
189
break
158
190
}
159
- go e .Job . Run ( )
191
+ go c . runWithRecovery ( e .Job )
160
192
e .Prev = e .Next
161
- e .Next = e .Schedule .Next (effective )
193
+ e .Next = e .Schedule .Next (now )
162
194
}
163
195
continue
164
196
165
197
case newEntry := <- c .add :
166
198
c .entries = append (c .entries , newEntry )
167
- newEntry .Next = newEntry .Schedule .Next (now )
199
+ newEntry .Next = newEntry .Schedule .Next (time . Now (). In ( c . location ) )
168
200
169
201
case <- c .snapshot :
170
202
c .snapshot <- c .entrySnapshot ()
171
203
172
204
case <- c .stop :
205
+ timer .Stop ()
173
206
return
174
207
}
175
208
176
209
// 'now' should be updated after newEntry and snapshot cases.
177
- now = time .Now ().Local ()
210
+ now = time .Now ().In (c .location )
211
+ timer .Stop ()
212
+ }
213
+ }
214
+
215
+ // Logs an error to stderr or to the configured error log
216
+ func (c * Cron ) logf (format string , args ... interface {}) {
217
+ if c .ErrorLog != nil {
218
+ c .ErrorLog .Printf (format , args ... )
219
+ } else {
220
+ log .Printf (format , args ... )
178
221
}
179
222
}
180
223
0 commit comments