New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
added a periodic worker #469
Conversation
… on an input duration
w := &periodicWorker{} | ||
go func() { | ||
for { | ||
err := doWork(w.tomb.Dying()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if err != nil
we should kill the tomb and return immediately
Thanks, but needs a few tweaks. |
for { | ||
err := doWork(w.tomb.Dying()) | ||
select { | ||
case <-time.Tick(sleepDuration): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
time.Tick is wrong here - it creates a new Ticker which is never destroyed,
which could become a serious memory leak
You want to use time.Timer. Something like this, perhaps?
t := time.NewTimer(0)
w := &periodicWorker{}
go func() {
defer w.tomb.Done()
for {
if err := doWork(w.tomb.Dying()); err != nil {
w.tomb.Kill(err)
return
}
t.Reset(sleepDuration)
select {
case <-t.C:
case <-w.tomb.Dying()
return
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great catch @rogpeppe, thanks very much
case <-w.tomb.Dying(): | ||
w.tomb.Kill(tomb.ErrDying) | ||
return | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This feels a little bit off. I'm thinking something more like:
type Callback func(stop <-chan struct{}) error
func NewPeriodicWorker(callback Callback, period time.Duration) Worker {
w := periodicWorker{}
go func() {
defer w.tomb.Done()
w.tomb.Kill(w.run(callback, period))
}
return w
}
func (w *periodicWorker) run(callback Callback, period time.Duration) error {
tick := time.After(0)
stop := w.tomb.Dying()
for {
select {
case <-stop:
return tomb.ErrDying
case <-tick:
if err := callback(stop); err != nil {
return err
}
tick = time.After(period)
}
}
}
} | ||
|
||
w := NewPeriodicWorker(doWork, time.Second) | ||
defer func() { c.Assert(Stop(w), gc.Equals, testError) }() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
your conscientiousness does you credit, I often get lazy and just skip error checking in those cases. I should stop doing that.
One significant concern re tomb behaviour, otherwise looking great. |
|
||
// test we can kill again without a panic | ||
w.Kill() | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is just what I was looking for, but would you parameterise it on the test error and run it twice please? Just so we can verify the trivial effect of a nil return from a stopped work func.
LGTM with one trivial variation on a test. |
|
Status: merge request accepted. Url: http://juju-ci.vapour.ws:8080/job/github-merge-juju |
added a periodic worker The periodic worker will call a function periodically based on an input duration
The periodic worker will call a function periodically based on an input duration