Skip to content

Commit 4ca6fd9

Browse files
committed
Supporting customize Timer to control the scheduling behavior
Thanks to PR robfig/cron#491
1 parent 92309eb commit 4ca6fd9

File tree

4 files changed

+55
-7
lines changed

4 files changed

+55
-7
lines changed

cron.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ type Cron struct {
2424
parser ScheduleParser
2525
nextID EntryID
2626
jobWaiter sync.WaitGroup
27+
28+
timerFn func(d time.Duration) Timer
2729
}
2830

2931
// ScheduleParser is an interface for schedule spec parsers that return a Schedule
@@ -123,6 +125,7 @@ func New(opts ...Option) *Cron {
123125
logger: DefaultLogger,
124126
location: time.Local,
125127
parser: standardParser,
128+
timerFn: newStdTimer,
126129
}
127130
for _, opt := range opts {
128131
opt(c)
@@ -250,7 +253,7 @@ func (c *Cron) run() {
250253
c.logger.Info("schedule", "now", now, "entry", entry.ID, "next", entry.Next)
251254
}
252255

253-
var timer *time.Timer
256+
var timer Timer
254257
for {
255258
// Determine the next entry to run.
256259
slices.SortFunc(c.entries, func(a, b *Entry) int { return a.Cmp(b) })
@@ -265,15 +268,15 @@ func (c *Cron) run() {
265268
}
266269

267270
if timer == nil {
268-
timer = time.NewTimer(d)
271+
timer = c.timerFn(d)
269272
} else {
270273
timer.Reset(d)
271274
}
272275

273276
var stopTimer bool
274277
for {
275278
select {
276-
case now = <-timer.C:
279+
case now = <-timer.C():
277280
now = now.In(c.location)
278281
c.logger.Info("wake", "now", now)
279282

@@ -314,7 +317,7 @@ func (c *Cron) run() {
314317
}
315318

316319
if stopTimer && !timer.Stop() {
317-
<-timer.C
320+
<-timer.C()
318321
}
319322
}
320323
}

option.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
package cron
22

3-
import (
4-
"time"
5-
)
3+
import "time"
64

75
// Option represents a modification to the default behavior of a Cron.
86
type Option func(*Cron)
@@ -43,3 +41,9 @@ func WithLogger(logger Logger) Option {
4341
c.logger = logger
4442
}
4543
}
44+
45+
// WithTimer specifies a [Timer] creator. If not specified, [time.Timer] will be
46+
// used by default. Refer to the [Timer] documentation for details.
47+
func WithTimer(fn func(time.Duration) Timer) Option {
48+
return func(c *Cron) { c.timerFn = fn }
49+
}

option_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"testing"
77
"time"
88

9+
"github.com/stretchr/testify/assert"
910
"github.com/stretchr/testify/require"
1011
)
1112

@@ -43,3 +44,15 @@ func TestWithVerboseLogger(t *testing.T) {
4344
t.Error("expected to see some actions, got:", out)
4445
}
4546
}
47+
48+
func TestWithTimer(t *testing.T) {
49+
var called bool
50+
timerFn := func(d time.Duration) Timer {
51+
called = true
52+
return newStdTimer(d)
53+
}
54+
c := New(WithTimer(timerFn))
55+
c.Start()
56+
<-c.Stop().Done()
57+
assert.True(t, called)
58+
}

timer.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package cron
2+
3+
import "time"
4+
5+
// A Timer works just like [time.Timer], and cron schedules [Jobs] by waiting
6+
// for the time event emitted by Timer. By default, [time.Timer] is used. You
7+
// can also customize a Timer with [WithTimer] option to control scheduling
8+
// behavior.
9+
type Timer interface {
10+
C() <-chan time.Time
11+
Reset(d time.Duration) bool
12+
Stop() bool
13+
}
14+
15+
// newStdTimer returns a Timer created using [time.Timer].
16+
func newStdTimer(d time.Duration) Timer {
17+
return &stdTimer{t: time.NewTimer(d)}
18+
}
19+
20+
type stdTimer struct {
21+
t *time.Timer
22+
}
23+
24+
func (self *stdTimer) C() <-chan time.Time { return self.t.C }
25+
26+
func (self *stdTimer) Reset(d time.Duration) bool { return self.t.Reset(d) }
27+
28+
func (self *stdTimer) Stop() bool { return self.t.Stop() }

0 commit comments

Comments
 (0)